Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
H
HuaweiPlugin
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Minhan Jeong
HuaweiPlugin
Commits
56a3b326
Commit
56a3b326
authored
Nov 19, 2021
by
Minhan Jeong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
화웨이 로그인 시스템 추가중
parent
b9a8ea41
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
2354 additions
and
15 deletions
+2354
-15
DefaultEngine.ini
Config/DefaultEngine.ini
+4
-2
Huawei.uproject
Huawei.uproject
+6
-0
AndroidPlayBilling.uplugin
Plugins/AndroidPlayBilling/AndroidPlayBilling.uplugin
+30
-0
Icon128.png
Plugins/AndroidPlayBilling/Resources/Icon128.png
+0
-0
.clang-format
Plugins/AndroidPlayBilling/Source/.clang-format
+127
-0
AndroidPlayBilling.java
...Source/AndroidPlayBilling/Android/AndroidPlayBilling.java
+623
-0
AndroidPlayBilling.Build.cs
...ing/Source/AndroidPlayBilling/AndroidPlayBilling.Build.cs
+19
-0
AndroidPlayBilling_UPL_Android.xml
...rce/AndroidPlayBilling/AndroidPlayBilling_UPL_Android.xml
+168
-0
AndroidPlayBilling.cpp
.../Source/AndroidPlayBilling/Private/AndroidPlayBilling.cpp
+40
-0
AndroidPlayBillingSettings.cpp
...AndroidPlayBilling/Private/AndroidPlayBillingSettings.cpp
+11
-0
AndroidPlayBillingSubsystem.cpp
...ndroidPlayBilling/Private/AndroidPlayBillingSubsystem.cpp
+645
-0
AndroidPlayBilling.h
...ing/Source/AndroidPlayBilling/Public/AndroidPlayBilling.h
+97
-0
AndroidPlayBillingSettings.h
...ce/AndroidPlayBilling/Public/AndroidPlayBillingSettings.h
+20
-0
AndroidPlayBillingSubsystem.h
...e/AndroidPlayBilling/Public/AndroidPlayBillingSubsystem.h
+519
-0
HuaweiSDK.zip
Plugins/HuaweiSDK.zip
+0
-0
HuaweiSDK_UPL.xml
Plugins/HuaweiSDK/Source/HuaweiSDK/HuaweiSDK_UPL.xml
+25
-6
AccountActivity.java
...weiSDK/Private/Android/Java/activity/AccountActivity.java
+14
-6
EntryActivity.java
...uaweiSDK/Private/Android/Java/activity/EntryActivity.java
+1
-1
HuaweiAccount.cpp
Plugins/HuaweiSDK/Source/HuaweiSDK/Private/HuaweiAccount.cpp
+5
-0
No files found.
Config/DefaultEngine.ini
View file @
56a3b326
...
...
@@ -145,8 +145,10 @@ KeyAlias=rmvw2021
KeyStorePassword
=
3df1q2w3e
KeyPassword
=
3df1q2w3e
ApplicationDisplayName
=
Real Madrid Virtual World
bBuildForArmV7
=
False
bBuildForArm64
=
True
bBuildForArmV7
=
True
bBuildForArm64
=
False
MinSDKVersion
=
26
TargetSDKVersion
=
30
[/Script/GooglePADEditor.GooglePADRuntimeSettings]
bOnlyDistribution
=
False
...
...
Huawei.uproject
View file @
56a3b326
...
...
@@ -33,6 +33,11 @@
"Linux",
"Mac"
]
},
{
"Name": "AndroidPlayBilling",
"Enabled": true,
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/ad0040f7dbbc45cebdcbc4b2c5982904"
}
]
}
\ No newline at end of file
Plugins/AndroidPlayBilling/AndroidPlayBilling.uplugin
0 → 100644
View file @
56a3b326
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "Android Play Billing",
"Description": "Android Play Billing plugin for Unreal Engine provides Google Play Billing methods for buying consumable, non-consumable products and subscriptions.",
"Category": "Android",
"CreatedBy": "Nikita Klimov",
"CreatedByURL": "https://github.com/kulichin",
"DocsURL": "https://github.com/kulichin/UnrealAndroidPlayBilling/wiki",
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/ad0040f7dbbc45cebdcbc4b2c5982904",
"SupportURL": "https://github.com/kulichin/UnrealAndroidPlayBilling/issues",
"EngineVersion": "4.25.0",
"CanContainContent": false,
"IsBetaVersion": true,
"Installed": true,
"Modules": [
{
"Name": "AndroidPlayBilling",
"Type": "Runtime",
"LoadingPhase": "Default",
"WhitelistPlatforms": [
"Win64",
"Mac",
"Android"
]
}
]
}
\ No newline at end of file
Plugins/AndroidPlayBilling/Resources/Icon128.png
0 → 100644
View file @
56a3b326
9.14 KB
Plugins/AndroidPlayBilling/Source/.clang-format
0 → 100644
View file @
56a3b326
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 132
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: true
ForEachMacros:
- for
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '.*\.generated\.h'
Priority: 100
- Regex: '.*(PCH).*'
Priority: -1
- Regex: '".*"'
Priority: 1
- Regex: '^<.*\.(h)>'
Priority: 3
- Regex: '^<.*>'
Priority: 4
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 4
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 4
UseTab: Always
...
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/Android/AndroidPlayBilling.java
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
package
com
.
kulichin
.
androidplaybilling
;
import
android.app.Activity
;
import
androidx.annotation.NonNull
;
import
com.android.billingclient.api.AccountIdentifiers
;
import
com.android.billingclient.api.AcknowledgePurchaseParams
;
import
com.android.billingclient.api.BillingClient
;
import
com.android.billingclient.api.BillingClientStateListener
;
import
com.android.billingclient.api.BillingFlowParams
;
import
com.android.billingclient.api.BillingResult
;
import
com.android.billingclient.api.ConsumeParams
;
import
com.android.billingclient.api.PriceChangeFlowParams
;
import
com.android.billingclient.api.Purchase
;
import
com.android.billingclient.api.PurchaseHistoryRecord
;
import
com.android.billingclient.api.PurchasesUpdatedListener
;
import
com.android.billingclient.api.SkuDetails
;
import
com.android.billingclient.api.SkuDetailsParams
;
import
com.android.billingclient.api.SkuDetailsResponseListener
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.List
;
public
class
AndroidPlayBilling
{
public
static
native
void
NativeCompleteOperation
(
int
ResponseCode
,
String
ResponseMessage
);
public
static
native
void
NativeCompleteLaunchBillingFlow
(
int
ResponseCode
,
String
ResponseMessage
,
PurchaseRecord
[]
Array
,
int
ArraySize
);
public
static
native
void
NativeCompleteQueryPurchaseHistory
(
int
ResponseCode
,
String
ResponseMessage
,
PurchaseRecordFromHistory
[]
Array
,
int
ArraySize
);
public
static
native
void
NativeCompleteQuerySkuDetails
(
int
ResponseCode
,
String
ResponseMessage
,
SkuDetailsRecord
[]
Array
,
int
ArraySize
);
public
static
native
void
NativeCompleteQueryPurchases
(
int
ResponseCode
,
String
ResponseMessage
,
PurchaseRecord
[]
Array
,
int
ArraySize
);
public
static
native
void
NativeInitialize
(
Class
SkuDetailsRecordClass
,
Class
PurchaseRecordFromHistoryClass
,
Class
PurchaseRecordClass
);
public
static
class
SkuDetailsRecord
{
public
String
OriginalJSON
;
public
String
ProductID
;
public
String
Type
;
public
String
Price
;
public
long
PriceAmountMicros
;
public
String
PriceCurrencyCode
;
public
String
OriginalPrice
;
public
long
OriginalPriceAmountMicros
;
public
String
Title
;
public
String
Description
;
public
String
SubscriptionPeriod
;
public
String
FreeTrialPeriod
;
public
String
IntroductoryPrice
;
public
long
IntroductoryPriceAmountMicros
;
public
String
IntroductoryPricePeriod
;
public
int
IntroductoryPriceCycles
;
public
String
IconURL
;
SkuDetailsRecord
(
String
OriginalJSON
,
String
ProductID
,
String
Type
,
String
Price
,
long
PriceAmountMicros
,
String
PriceCurrencyCode
,
String
OriginalPrice
,
long
OriginalPriceAmountMicros
,
String
Title
,
String
Description
,
String
SubscriptionPeriod
,
String
FreeTrialPeriod
,
String
IntroductoryPrice
,
long
IntroductoryPriceAmountMicros
,
String
IntroductoryPricePeriod
,
int
IntroductoryPriceCycles
,
String
IconURL
)
{
this
.
OriginalJSON
=
OriginalJSON
;
this
.
ProductID
=
ProductID
;
this
.
Type
=
Type
;
this
.
Price
=
Price
;
this
.
PriceAmountMicros
=
PriceAmountMicros
;
this
.
PriceCurrencyCode
=
PriceCurrencyCode
;
this
.
OriginalPrice
=
OriginalPrice
;
this
.
OriginalPriceAmountMicros
=
OriginalPriceAmountMicros
;
this
.
Title
=
Title
;
this
.
Description
=
Description
;
this
.
SubscriptionPeriod
=
SubscriptionPeriod
;
this
.
FreeTrialPeriod
=
FreeTrialPeriod
;
this
.
IntroductoryPrice
=
IntroductoryPrice
;
this
.
IntroductoryPriceAmountMicros
=
IntroductoryPriceAmountMicros
;
this
.
IntroductoryPricePeriod
=
IntroductoryPricePeriod
;
this
.
IntroductoryPriceCycles
=
IntroductoryPriceCycles
;
this
.
IconURL
=
IconURL
;
}
}
public
static
class
PurchaseRecordFromHistory
{
public
String
ProductID
;
public
long
PurchaseTime
;
public
String
PurchaseToken
;
public
String
DeveloperPayload
;
public
String
OriginalJSON
;
public
String
Signature
;
PurchaseRecordFromHistory
(
String
ProductID
,
long
PurchaseTime
,
String
PurchaseToken
,
String
DeveloperPayload
,
String
OriginalJSON
,
String
Signature
)
{
this
.
ProductID
=
ProductID
;
this
.
PurchaseTime
=
PurchaseTime
;
this
.
PurchaseToken
=
PurchaseToken
;
this
.
DeveloperPayload
=
DeveloperPayload
;
this
.
OriginalJSON
=
OriginalJSON
;
this
.
Signature
=
Signature
;
}
}
public
static
class
PurchaseRecord
{
public
String
OrderID
;
public
String
PackageName
;
public
String
ProductID
;
public
long
PurchaseTime
;
public
String
PurchaseToken
;
public
int
PurchaseState
;
public
String
DeveloperPayload
;
public
boolean
bAcknowledged
;
public
boolean
bAutoRenewing
;
public
String
OriginalJSON
;
public
String
Signature
;
public
String
ObfuscatedAccountID
;
public
String
ObfuscatedProfileID
;
PurchaseRecord
(
String
OrderID
,
String
PackageName
,
String
ProductID
,
long
PurchaseTime
,
String
PurchaseToken
,
int
PurchaseState
,
String
DeveloperPayload
,
boolean
bAcknowledged
,
boolean
bAutoRenewing
,
String
OriginalJSON
,
String
Signature
,
String
ObfuscatedAccountID
,
String
ObfuscatedProfileID
)
{
this
.
OrderID
=
OrderID
;
this
.
PackageName
=
PackageName
;
this
.
ProductID
=
ProductID
;
this
.
PurchaseTime
=
PurchaseTime
;
this
.
PurchaseToken
=
PurchaseToken
;
this
.
PurchaseState
=
PurchaseState
;
this
.
DeveloperPayload
=
DeveloperPayload
;
this
.
bAcknowledged
=
bAcknowledged
;
this
.
bAutoRenewing
=
bAutoRenewing
;
this
.
OriginalJSON
=
OriginalJSON
;
this
.
Signature
=
Signature
;
this
.
ObfuscatedAccountID
=
ObfuscatedAccountID
;
this
.
ObfuscatedProfileID
=
ObfuscatedProfileID
;
}
}
private
final
int
PRODUCT_INAPP_ID
=
0
;
private
final
int
PRODUCT_SUBS_ID
=
1
;
private
final
String
[]
ProductTypes
=
{
"inapp"
,
"subs"
};
private
final
String
[]
FeatureTypes
=
{
"inAppItemsOnVr"
,
"priceChangeConfirmation"
,
"subscriptions"
,
"subscriptionsOnVr"
,
"subscriptionsUpdate"
};
private
final
Activity
GameActivity
;
private
final
HashMap
<
String
,
PurchaseRecord
>
PurchaseRecords
=
new
HashMap
<>();
private
final
BillingClient
AppBillingClient
;
public
AndroidPlayBilling
(
Activity
GameActivity
)
{
this
.
GameActivity
=
GameActivity
;
final
PurchasesUpdatedListener
PurchasesListener
=
(
Result
,
Purchases
)
->
{
PurchaseRecord
[]
WrappedPurchaseRecords
=
null
;
int
WrappedPurchaseRecordsSize
=
0
;
if
(
Purchases
!=
null
)
{
WrappedPurchaseRecords
=
new
PurchaseRecord
[
Purchases
.
size
()];
WrappedPurchaseRecordsSize
=
Purchases
.
size
();
for
(
int
Idx
=
0
;
Idx
<
Purchases
.
size
();
Idx
++)
{
// Wrap to our class
Purchase
PurchaseRecord
=
Purchases
.
get
(
Idx
);
PurchaseRecord
WrappedPurchaseRecord
=
WrapToPurchaseRecord
(
PurchaseRecord
);
// Put record to hash map
String
ProductID
=
WrappedPurchaseRecord
.
ProductID
;
PurchaseRecords
.
put
(
ProductID
,
WrappedPurchaseRecord
);
// Append to 'WrappedPurchaseRecords'
WrappedPurchaseRecords
[
Idx
]
=
WrappedPurchaseRecord
;
// Handle purchase
HandlePurchaseInternal
(
PurchaseRecord
);
}
}
// Complete operation
NativeCompleteLaunchBillingFlow
(
Result
.
getResponseCode
(),
Result
.
getDebugMessage
(),
WrappedPurchaseRecords
,
WrappedPurchaseRecordsSize
);
};
// Initialize billing client
AppBillingClient
=
BillingClient
.
newBuilder
(
GameActivity
)
.
setListener
(
PurchasesListener
)
.
enablePendingPurchases
()
.
build
();
AppBillingClient
.
startConnection
(
new
BillingClientStateListener
()
{
@Override
public
void
onBillingSetupFinished
(
@NonNull
BillingResult
Result
)
{
QueryBillingDetailsInternal
();
}
// TODO: try to restart google billing
@Override
public
void
onBillingServiceDisconnected
()
{
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
// Call native initializator
NativeInitialize
(
SkuDetailsRecord
.
class
,
PurchaseRecordFromHistory
.
class
,
PurchaseRecord
.
class
);
}
public
void
OnDestroy
()
{
AppBillingClient
.
endConnection
();
}
public
void
LaunchBillingFlow
(
String
ProductID
,
int
ProductType
)
{
// Query Sku details & start purchase
QuerySkuDetailsInternal
(
new
String
[]
{
ProductID
},
ProductType
,
(
QueryResult
,
SkuDetailsList
)
->
{
SkuDetails
Details
=
ConstructSkuDetailsInternal
(
ProductID
,
ProductType
);
if
(
SkuDetailsList
!=
null
&&
SkuDetailsList
.
size
()
>
0
)
{
Details
=
SkuDetailsList
.
get
(
0
);
}
// Build BillingFlowParams
BillingFlowParams
FlowParams
=
BillingFlowParams
.
newBuilder
()
.
setSkuDetails
(
Details
)
.
build
();
BillingResult
FlowResult
=
AppBillingClient
.
launchBillingFlow
(
GameActivity
,
FlowParams
);
if
(
FlowResult
.
getResponseCode
()
!=
BillingClient
.
BillingResponseCode
.
OK
)
{
// Complete operation
NativeCompleteLaunchBillingFlow
(
FlowResult
.
getResponseCode
(),
FlowResult
.
getDebugMessage
(),
null
,
0
);
}
// else: wait for 'PurchasesListener' callback
});
}
public
void
LaunchPriceChangeConfirmationFlow
(
String
ProductID
,
int
ProductType
)
{
// Query Sku details & start purchase
QuerySkuDetailsInternal
(
new
String
[]
{
ProductID
},
ProductType
,
(
QueryResult
,
SkuDetailsList
)
->
{
SkuDetails
Details
=
ConstructSkuDetailsInternal
(
ProductID
,
ProductType
);
if
(
SkuDetailsList
!=
null
&&
SkuDetailsList
.
size
()
>
0
)
{
Details
=
SkuDetailsList
.
get
(
0
);
}
// Build PriceChangeParams
PriceChangeFlowParams
PriceChangeParams
=
PriceChangeFlowParams
.
newBuilder
()
.
setSkuDetails
(
Details
)
.
build
();
AppBillingClient
.
launchPriceChangeConfirmationFlow
(
GameActivity
,
PriceChangeParams
,
FlowResult
->
// Complete operation
NativeCompleteOperation
(
FlowResult
.
getResponseCode
(),
FlowResult
.
getDebugMessage
()));
});
}
public
void
QueryPurchases
(
int
ProductType
)
{
Purchase
.
PurchasesResult
Result
=
QueryPurchasesInternal
(
ProductType
);
List
<
Purchase
>
Purchases
=
Result
.
getPurchasesList
();
if
(
Result
.
getResponseCode
()
==
BillingClient
.
BillingResponseCode
.
OK
&&
Purchases
!=
null
)
{
// Construct array for wrapped records
int
PurchasesSize
=
Purchases
.
size
();
PurchaseRecord
[]
Records
=
new
PurchaseRecord
[
PurchasesSize
];
for
(
int
Idx
=
0
;
Idx
<
PurchasesSize
;
Idx
++)
{
Records
[
Idx
]
=
WrapToPurchaseRecord
(
Purchases
.
get
(
Idx
));
}
// Complete operation
NativeCompleteQueryPurchases
(
Result
.
getResponseCode
(),
Result
.
getBillingResult
().
getDebugMessage
(),
Records
,
Records
.
length
);
}
else
{
// Complete operation
NativeCompleteQueryPurchases
(
Result
.
getResponseCode
(),
Result
.
getBillingResult
().
getDebugMessage
(),
null
,
0
);
}
}
public
void
QuerySkuDetails
(
String
[]
ProductIDs
,
int
ProductType
)
{
// Query Sku details & start purchase
QuerySkuDetailsInternal
(
ProductIDs
,
ProductType
,
(
Result
,
SkuDetailsList
)
->
{
if
(
Result
.
getResponseCode
()
==
BillingClient
.
BillingResponseCode
.
OK
&&
SkuDetailsList
!=
null
)
{
final
int
SkuDetailsSize
=
SkuDetailsList
.
size
();
SkuDetailsRecord
[]
DetailsRecords
=
new
SkuDetailsRecord
[
SkuDetailsSize
];
for
(
int
Idx
=
0
;
Idx
<
SkuDetailsSize
;
Idx
++)
{
SkuDetails
Details
=
SkuDetailsList
.
get
(
Idx
);
DetailsRecords
[
Idx
]
=
WrapToSkuDetailsRecord
(
Details
);
}
// Complete operation
NativeCompleteQuerySkuDetails
(
Result
.
getResponseCode
(),
Result
.
getDebugMessage
(),
DetailsRecords
,
DetailsRecords
.
length
);
}
else
{
// Complete operation
NativeCompleteQuerySkuDetails
(
Result
.
getResponseCode
(),
Result
.
getDebugMessage
(),
null
,
0
);
}
});
}
public
void
QueryPurchaseHistory
(
int
ProductType
)
{
AppBillingClient
.
queryPurchaseHistoryAsync
(
ProductTypes
[
ProductType
],
(
Result
,
Purchases
)
->
{
if
(
Result
.
getResponseCode
()
==
BillingClient
.
BillingResponseCode
.
OK
&&
Purchases
!=
null
)
{
// Construct array for wrapped records
int
PurchasesSize
=
Purchases
.
size
();
PurchaseRecordFromHistory
[]
Records
=
new
PurchaseRecordFromHistory
[
PurchasesSize
];
for
(
int
Idx
=
0
;
Idx
<
PurchasesSize
;
Idx
++)
{
Records
[
Idx
]
=
WrapToPurchaseRecordFromHistory
(
Purchases
.
get
(
Idx
));
}
// Complete operation
NativeCompleteQueryPurchaseHistory
(
Result
.
getResponseCode
(),
Result
.
getDebugMessage
(),
Records
,
Records
.
length
);
}
else
{
// Complete operation
NativeCompleteQueryPurchaseHistory
(
Result
.
getResponseCode
(),
Result
.
getDebugMessage
(),
null
,
0
);
}
});
}
public
int
IsFeatureSupported
(
int
FeatureType
)
{
BillingResult
Result
=
AppBillingClient
.
isFeatureSupported
(
FeatureTypes
[
FeatureType
]);
return
Result
.
getResponseCode
();
}
public
boolean
IsIAPInitialized
()
{
if
(
AppBillingClient
!=
null
)
{
return
AppBillingClient
.
isReady
();
}
return
false
;
}
public
boolean
IsPurchased
(
String
ProductID
)
{
return
PurchaseRecords
.
containsKey
(
ProductID
);
}
// Internal
private
Purchase
.
PurchasesResult
QueryPurchasesInternal
(
int
ProductType
)
{
return
AppBillingClient
.
queryPurchases
(
ProductTypes
[
ProductType
]);
}
private
void
QueryBillingDetailsInternal
()
{
// Query purchases on resume
Purchase
.
PurchasesResult
SubscriptionsResult
=
QueryPurchasesInternal
(
PRODUCT_SUBS_ID
);
Purchase
.
PurchasesResult
ProductsResult
=
QueryPurchasesInternal
(
PRODUCT_INAPP_ID
);
// Handle purchases
for
(
Purchase
Subscription
:
SubscriptionsResult
.
getPurchasesList
())
{
HandlePurchaseInternal
(
Subscription
);
}
for
(
Purchase
Product
:
ProductsResult
.
getPurchasesList
())
{
HandlePurchaseInternal
(
Product
);
}
}
private
SkuDetails
ConstructSkuDetailsInternal
(
String
ProductID
,
int
ProductType
)
{
try
{
String
SkuDetailsJSON
=
new
JSONObject
()
.
put
(
"productId"
,
ProductID
)
.
put
(
"type"
,
ProductTypes
[
ProductType
])
.
toString
();
return
new
SkuDetails
(
SkuDetailsJSON
);
}
catch
(
JSONException
ignored
)
{
return
null
;
}
}
// Internal
private
void
QuerySkuDetailsInternal
(
String
[]
ProductIDs
,
int
ProductType
,
SkuDetailsResponseListener
SkuDetailsListener
)
{
// Build Sku details
List
<
String
>
SkuList
=
Arrays
.
asList
(
ProductIDs
);
SkuDetailsParams
SkuDetails
=
SkuDetailsParams
.
newBuilder
()
.
setType
(
ProductTypes
[
ProductType
])
.
setSkusList
(
SkuList
)
.
build
();
// Query Sku details & start purchase
AppBillingClient
.
querySkuDetailsAsync
(
SkuDetails
,
SkuDetailsListener
);
}
public
void
HandlePurchaseInternal
(
Purchase
PurchaseRecord
)
{
if
(
PurchaseRecord
!=
null
&&
PurchaseRecord
.
getDeveloperPayload
().
isEmpty
())
{
// Try to consume item
ConsumeParams
Params
=
ConsumeParams
.
newBuilder
()
.
setPurchaseToken
(
PurchaseRecord
.
getPurchaseToken
())
.
build
();
AppBillingClient
.
consumeAsync
(
Params
,
(
ConsumeResult
,
PurchaseToken
)
->
{
// If item not consumed then acknowledge them
if
(
ConsumeResult
.
getResponseCode
()
!=
BillingClient
.
BillingResponseCode
.
OK
)
{
AcknowledgePurchaseParams
AcknowledgeParams
=
AcknowledgePurchaseParams
.
newBuilder
()
.
setPurchaseToken
(
PurchaseRecord
.
getPurchaseToken
())
.
build
();
AppBillingClient
.
acknowledgePurchase
(
AcknowledgeParams
,
AcknowledgeResult
->
{});
}
});
}
}
// Wrappers
private
PurchaseRecord
WrapToPurchaseRecord
(
Purchase
Record
)
{
// Check validity of 'getAccountIdentifiers'
String
ObfuscatedAccountID
=
""
;
String
ObfuscatedProfileID
=
""
;
AccountIdentifiers
Identifiers
=
Record
.
getAccountIdentifiers
();
if
(
Identifiers
!=
null
)
{
ObfuscatedAccountID
=
Identifiers
.
getObfuscatedAccountId
();
ObfuscatedProfileID
=
Identifiers
.
getObfuscatedProfileId
();
}
return
new
PurchaseRecord
(
Record
.
getOrderId
(),
Record
.
getPackageName
(),
Record
.
getSkus
().
get
(
0
),
Record
.
getPurchaseTime
(),
Record
.
getPurchaseToken
(),
Record
.
getPurchaseState
(),
Record
.
getDeveloperPayload
(),
Record
.
isAcknowledged
(),
Record
.
isAutoRenewing
(),
Record
.
getOriginalJson
(),
Record
.
getSignature
(),
ObfuscatedAccountID
,
ObfuscatedProfileID
);
}
private
PurchaseRecordFromHistory
WrapToPurchaseRecordFromHistory
(
PurchaseHistoryRecord
Record
)
{
return
new
PurchaseRecordFromHistory
(
Record
.
getSkus
().
get
(
0
),
Record
.
getPurchaseTime
(),
Record
.
getPurchaseToken
(),
Record
.
getDeveloperPayload
(),
Record
.
getOriginalJson
(),
Record
.
getSignature
()
);
}
private
SkuDetailsRecord
WrapToSkuDetailsRecord
(
SkuDetails
Record
)
{
return
new
SkuDetailsRecord
(
Record
.
getOriginalJson
(),
Record
.
getSku
(),
Record
.
getType
(),
Record
.
getPrice
(),
Record
.
getPriceAmountMicros
(),
Record
.
getPriceCurrencyCode
(),
Record
.
getOriginalPrice
(),
Record
.
getOriginalPriceAmountMicros
(),
Record
.
getTitle
(),
Record
.
getDescription
(),
Record
.
getSubscriptionPeriod
(),
Record
.
getFreeTrialPeriod
(),
Record
.
getIntroductoryPrice
(),
Record
.
getIntroductoryPriceAmountMicros
(),
Record
.
getIntroductoryPricePeriod
(),
Record
.
getIntroductoryPriceCycles
(),
Record
.
getIconUrl
()
);
}
}
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/AndroidPlayBilling.Build.cs
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
using
UnrealBuildTool
;
public
class
AndroidPlayBilling
:
ModuleRules
{
public
AndroidPlayBilling
(
ReadOnlyTargetRules
Target
)
:
base
(
Target
)
{
PCHUsage
=
ModuleRules
.
PCHUsageMode
.
UseExplicitOrSharedPCHs
;
PublicDependencyModuleNames
.
AddRange
(
new
string
[]
{
"Core"
,
"CoreUObject"
,
"Engine"
});
string
PluginPath
=
Utils
.
MakePathRelativeTo
(
ModuleDirectory
,
Target
.
RelativeEnginePath
);
if
(
Target
.
Platform
==
UnrealTargetPlatform
.
Android
)
{
PrivateDependencyModuleNames
.
Add
(
"Launch"
);
AdditionalPropertiesForReceipt
.
Add
(
"AndroidPlugin"
,
System
.
IO
.
Path
.
Combine
(
PluginPath
,
"AndroidPlayBilling_UPL_Android.xml"
));
}
}
}
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/AndroidPlayBilling_UPL_Android.xml
0 → 100644
View file @
56a3b326
<?xml version="1.0" encoding="utf-8"?>
<root
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
>
<!-- Add dependencies -->
<androidManifestUpdates>
<addPermission
android:name=
"android.permission.INTERNET"
/>
<addPermission
android:name=
"com.android.vending.BILLING"
/>
</androidManifestUpdates>
<!-- Copy Library directory & google-services.json -->
<gradleCopies>
<copyDir
src=
"$S(PluginDir)/Android/"
dst=
"$S(BuildDir)/gradle/app/src/main/java/com/kulichin/androidplaybilling/"
/>
</gradleCopies>
<!-- Import dependencies -->
<AARImports>
<insertValue
value=
"com.android.billingclient,billing,4.0.0"
/>
<insertNewline
/>
</AARImports>
<!--Enable AndroidX support-->
<gradleProperties>
<insert>
android.useAndroidX = true
android.enableJetifier = true
</insert>
</gradleProperties>
<!-- Replace supportlib imports with AndroidX in engine source files -->
<baseBuildGradleAdditions>
<insert>
allprojects
{
def mappings =
[
'android.arch.lifecycle.Lifecycle': 'androidx.lifecycle.Lifecycle',
'android.arch.lifecycle.LifecycleObserver': 'androidx.lifecycle.LifecycleObserver',
'android.arch.lifecycle.OnLifecycleEvent': 'androidx.lifecycle.OnLifecycleEvent',
'android.arch.lifecycle.ProcessLifecycleOwner': 'androidx.lifecycle.ProcessLifecycleOwner',
'android.arch.lifecycle': 'androidx.lifecycle',
'android.support.annotation': 'androidx.annotation',
'android.support.v13.app.FragmentCompat': 'androidx.legacy.app.FragmentCompat',
'android.support.v4.app.ActivityCompat': 'androidx.core.app.ActivityCompat',
'android.support.v4.app.NotificationCompat': 'androidx.core.app.NotificationCompat',
'android.support.v4.app.NotificationManagerCompat': 'androidx.core.app.NotificationManagerCompat',
'android.support.v4.content.ContextCompat': 'androidx.core.content.ContextCompat',
'android.support.v4.content.FileProvider': 'androidx.core.content.FileProvider',
'constProduct.getSku()': 'constProduct.getSkus().get(0)',
'ownedProduct.getSku()': 'ownedProduct.getSkus().get(0)',
'purchase.getSku()': 'purchase.getSkus().get(0)',
]
beforeEvaluate { project ->
project.rootProject.projectDir.traverse(type: groovy.io.FileType.FILES, nameFilter: ~/.*\.java$/) { f ->
mappings.each { entry ->
if (f.getText('UTF-8').contains(entry.key)) {
println "Updating ${entry.key} to ${entry.value} in file ${f}"
ant.replace(file: f, token: entry.key, value: entry.value)
}
}
}
}
}
</insert>
</baseBuildGradleAdditions>
<!-- Gradle additions -->
<buildGradleAdditions>
<insert>
android
{
compileOptions
{
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
</insert>
</buildGradleAdditions>
<!-- ProGuard additions -->
<proguardAdditions>
<insert>
-dontwarn com.kulichin.**
-keep class com.kulichin.** { *; }
-keep interface com.kulichin.** { *; }
-dontwarn com.android.billingclient.**
-keep class com.android.billingclient.** { *; }
-keep interface com.android.billingclient.** { *; }
</insert>
</proguardAdditions>
<!-- GameActivity.java additions -->
<gameActivityImportAdditions>
<insert>
import com.kulichin.androidplaybilling.AndroidPlayBilling;
</insert>
</gameActivityImportAdditions>
<!-- GameActivity.java additions -->
<gameActivityOnCreateAdditions>
<insert>
// Initialize billing class
<!-- PlayBilling = new AndroidPlayBilling(this);-->
</insert>
</gameActivityOnCreateAdditions>
<!-- GameActivity.java additions -->
<gameActivityOnDestroyAdditions>
<insert>
PlayBilling.OnDestroy();
</insert>
</gameActivityOnDestroyAdditions>
<!-- GameActivity.java additions -->
<gameActivityClassAdditions>
<insert>
private AndroidPlayBilling PlayBilling;
public void AndroidThunkJava_LaunchBillingFlow(String ProductID, int ProductType)
{
PlayBilling.LaunchBillingFlow(ProductID, ProductType);
}
public void AndroidThunkJava_LaunchPriceChangeConfirmationFlow(String ProductID, int ProductType)
{
PlayBilling.LaunchPriceChangeConfirmationFlow(ProductID, ProductType);
}
public void AndroidThunkJava_QueryPurchases(int ProductType)
{
PlayBilling.QueryPurchases(ProductType);
}
public void AndroidThunkJava_QuerySkuDetails(String[] ProductIDs, int ProductType)
{
PlayBilling.QuerySkuDetails(ProductIDs, ProductType);
}
public void AndroidThunkJava_QueryPurchaseHistory(int ProductType)
{
PlayBilling.QueryPurchaseHistory(ProductType);
}
public int AndroidThunkJava_IsFeatureSupported(int FeatureType)
{
return PlayBilling.IsFeatureSupported(FeatureType);
}
public boolean AndroidThunkJava_IsIAPInitialized()
{
return PlayBilling.IsIAPInitialized();
}
public boolean AndroidThunkJava_IsPurchased(String ProductID)
{
return PlayBilling.IsPurchased(ProductID);
}
</insert>
</gameActivityClassAdditions>
</root>
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/Private/AndroidPlayBilling.cpp
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
#include "AndroidPlayBilling.h"
#include "AndroidPlayBillingSettings.h"
#include "Settings/Public/ISettingsModule.h"
#define LOCTEXT_NAMESPACE "FAndroidPlayBillingModule"
void
FAndroidPlayBillingModule
::
StartupModule
()
{
if
(
ISettingsModule
*
SettingsModule
=
FModuleManager
::
GetModulePtr
<
ISettingsModule
>
(
"Settings"
))
{
SettingsModule
->
RegisterSettings
(
TEXT
(
"Project"
),
TEXT
(
"Plugins"
),
TEXT
(
"Android Play Billing"
),
LOCTEXT
(
"Android Play Billing"
,
"Android Play Billing"
),
LOCTEXT
(
"Android Play Billing"
,
"Settings for Android Play Billing"
),
GetMutableDefault
<
UAndroidPlayBillingSettings
>
());
}
}
void
FAndroidPlayBillingModule
::
ShutdownModule
()
{
if
(
ISettingsModule
*
SettingsModule
=
FModuleManager
::
GetModulePtr
<
ISettingsModule
>
(
"Settings"
))
{
SettingsModule
->
UnregisterSettings
(
TEXT
(
"Project"
),
TEXT
(
"Plugins"
),
TEXT
(
"AndroidPlayBilling"
));
}
}
FAndroidPlayBillingModule
*
FAndroidPlayBillingModule
::
GetModule
()
{
return
FModuleManager
::
Get
().
GetModulePtr
<
FAndroidPlayBillingModule
>
(
"AndroidPlayBilling"
);
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE
(
FAndroidPlayBillingModule
,
AndroidPlayBilling
)
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/Private/AndroidPlayBillingSettings.cpp
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
#include "AndroidPlayBillingSettings.h"
#if WITH_EDITOR
void
UAndroidPlayBillingSettings
::
PostEditChangeProperty
(
struct
FPropertyChangedEvent
&
PropertyChangedEvent
)
{
Super
::
PostEditChangeProperty
(
PropertyChangedEvent
);
SaveConfig
(
CPF_Config
,
*
GetDefaultConfigFilename
());
}
#endif
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/Private/AndroidPlayBillingSubsystem.cpp
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
#include "AndroidPlayBillingSubsystem.h"
#if PLATFORM_ANDROID
#include "AndroidPlayBilling.h"
#include "Android/AndroidJNI.h"
#include "Android/AndroidApplication.h"
// SkuDetails class fields
static
jfieldID
SkuDetailsRecord_OriginalJSON_FieldID
;
static
jfieldID
SkuDetailsRecord_ProductID_FieldID
;
static
jfieldID
SkuDetailsRecord_Type_FieldID
;
static
jfieldID
SkuDetailsRecord_Price_FieldID
;
static
jfieldID
SkuDetailsRecord_PriceAmountMicros_FieldID
;
static
jfieldID
SkuDetailsRecord_PriceCurrencyCode_FieldID
;
static
jfieldID
SkuDetailsRecord_OriginalPrice_FieldID
;
static
jfieldID
SkuDetailsRecord_OriginalPriceAmountMicros_FieldID
;
static
jfieldID
SkuDetailsRecord_Title_FieldID
;
static
jfieldID
SkuDetailsRecord_Description_FieldID
;
static
jfieldID
SkuDetailsRecord_SubscriptionPeriod_FieldID
;
static
jfieldID
SkuDetailsRecord_FreeTrialPeriod_FieldID
;
static
jfieldID
SkuDetailsRecord_IntroductoryPrice_FieldID
;
static
jfieldID
SkuDetailsRecord_IntroductoryPriceAmountMicros_FieldID
;
static
jfieldID
SkuDetailsRecord_IntroductoryPricePeriod_FieldID
;
static
jfieldID
SkuDetailsRecord_IntroductoryPriceCycles_FieldID
;
static
jfieldID
SkuDetailsRecord_IconURL_FieldID
;
// PurchaseRecordFromHistory class fields
static
jfieldID
PurchaseRecordFromHistory_ProductID_FieldID
;
static
jfieldID
PurchaseRecordFromHistory_PurchaseTime_FieldID
;
static
jfieldID
PurchaseRecordFromHistory_PurchaseToken_FieldID
;
static
jfieldID
PurchaseRecordFromHistory_DeveloperPayload_FieldID
;
static
jfieldID
PurchaseRecordFromHistory_OriginalJSON_FieldID
;
static
jfieldID
PurchaseRecordFromHistory_Signature_FieldID
;
// PurchaseRecord class fields
static
jfieldID
PurchaseRecord_OrderID_FieldID
;
static
jfieldID
PurchaseRecord_PackageName_FieldID
;
static
jfieldID
PurchaseRecord_ProductID_FieldID
;
static
jfieldID
PurchaseRecord_PurchaseTime_FieldID
;
static
jfieldID
PurchaseRecord_PurchaseToken_FieldID
;
static
jfieldID
PurchaseRecord_PurchaseState_FieldID
;
static
jfieldID
PurchaseRecord_DeveloperPayload_FieldID
;
static
jfieldID
PurchaseRecord_bAcknowledged_FieldID
;
static
jfieldID
PurchaseRecord_bAutoRenewing_FieldID
;
static
jfieldID
PurchaseRecord_OriginalJSON_FieldID
;
static
jfieldID
PurchaseRecord_Signature_FieldID
;
static
jfieldID
PurchaseRecord_ObfuscatedAccountID_FieldID
;
static
jfieldID
PurchaseRecord_ObfuscatedProfileID_FieldID
;
// Google Play Billing methods
static
jmethodID
LaunchBillingFlow_MethodID
;
static
jmethodID
LaunchPriceChangeConfirmationFlow_MethodID
;
static
jmethodID
QueryPurchases_MethodID
;
static
jmethodID
QuerySkuDetails_MethodID
;
static
jmethodID
QueryPurchaseHistory_MethodID
;
static
jmethodID
IsFeatureSupported_MethodID
;
static
jmethodID
IsIAPInitialized_MethodID
;
static
jmethodID
IsPurchased_MethodID
;
static
inline
FScopedJavaObject
<
jstring
>
GetScopedStringFromObject
(
JNIEnv
*
Env
,
jobject
Object
,
jfieldID
FieldID
)
{
if
(
FieldID
==
NULL
)
{
return
FScopedJavaObject
<
jstring
>
(
Env
,
Env
->
NewStringUTF
(
""
));
}
return
NewScopedJavaObject
(
Env
,
(
jstring
)
Env
->
GetObjectField
(
Object
,
FieldID
));
}
static
jmethodID
FindMethod
(
JNIEnv
*
Env
,
const
char
*
Name
,
const
char
*
Signature
)
{
if
(
Env
&&
Name
&&
Signature
)
{
return
Env
->
GetMethodID
(
FJavaWrapper
::
GameActivityClassID
,
Name
,
Signature
);
}
return
nullptr
;
}
static
jfieldID
FindField
(
JNIEnv
*
Env
,
jclass
Class
,
const
char
*
Name
,
const
char
*
Type
)
{
if
(
Env
&&
Name
&&
Type
&&
Class
)
{
return
Env
->
GetFieldID
(
Class
,
Name
,
Type
);
}
return
nullptr
;
}
static
bool
CallBooleanMethod
(
JNIEnv
*
Env
,
jmethodID
Method
,
...)
{
// make sure the function exists
jobject
Object
=
FJavaWrapper
::
GameActivityThis
;
if
(
Method
==
NULL
||
Object
==
NULL
||
Env
==
NULL
)
{
return
false
;
}
va_list
Args
;
va_start
(
Args
,
Method
);
return
Env
->
CallBooleanMethodV
(
Object
,
Method
,
Args
);
va_end
(
Args
);
}
static
bool
CallIntMethod
(
JNIEnv
*
Env
,
jmethodID
Method
,
...)
{
// make sure the function exists
jobject
Object
=
FJavaWrapper
::
GameActivityThis
;
if
(
Method
==
NULL
||
Object
==
NULL
||
Env
==
NULL
)
{
return
false
;
}
va_list
Args
;
va_start
(
Args
,
Method
);
return
Env
->
CallIntMethodV
(
Object
,
Method
,
Args
);
va_end
(
Args
);
}
static
void
CallVoidMethod
(
JNIEnv
*
Env
,
jmethodID
Method
,
...)
{
// make sure the function exists
jobject
Object
=
FJavaWrapper
::
GameActivityThis
;
if
(
Method
==
NULL
||
Object
==
NULL
||
Env
==
NULL
)
{
return
;
}
va_list
Args
;
va_start
(
Args
,
Method
);
Env
->
CallVoidMethodV
(
Object
,
Method
,
Args
);
va_end
(
Args
);
}
static
jobject
CallObjectMethod
(
JNIEnv
*
Env
,
jmethodID
Method
)
{
// make sure the function exists
jobject
Object
=
FJavaWrapper
::
GameActivityThis
;
if
(
Method
==
NULL
||
Object
==
NULL
||
Env
==
NULL
)
{
return
NULL
;
}
return
Env
->
CallObjectMethod
(
Object
,
Method
);
}
FPurchaseRecord
GetPurchaseRecordStructFromJavaObject
(
JNIEnv
*
Env
,
jobject
Object
)
{
if
(
Object
==
NULL
||
Env
==
NULL
)
{
return
{};
}
// PurchaseRecord class fields
auto
JOrderID
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_OrderID_FieldID
);
auto
JPackageName
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_PackageName_FieldID
);
auto
JProductID
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_ProductID_FieldID
);
int64
PurchaseTime
=
(
int64
)
Env
->
GetLongField
(
Object
,
PurchaseRecord_PurchaseTime_FieldID
);
auto
JPurchaseToken
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_PurchaseToken_FieldID
);
EPurchaseState
PurchaseState
=
(
EPurchaseState
)
Env
->
GetIntField
(
Object
,
PurchaseRecord_PurchaseState_FieldID
);
auto
JDeveloperPayload
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_DeveloperPayload_FieldID
);
bool
bAcknowledged
=
(
bool
)
Env
->
GetBooleanField
(
Object
,
PurchaseRecord_bAcknowledged_FieldID
);
bool
bAutoRenewing
=
(
bool
)
Env
->
GetBooleanField
(
Object
,
PurchaseRecord_bAutoRenewing_FieldID
);
auto
JOriginalJSON
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_OriginalJSON_FieldID
);
auto
JSignature
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_Signature_FieldID
);
auto
JObfuscatedAccountID
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_ObfuscatedAccountID_FieldID
);
auto
JObfuscatedProfileID
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecord_ObfuscatedProfileID_FieldID
);
// Construct FPurchaseRecord struct
return
{
FJavaHelper
::
FStringFromParam
(
Env
,
*
JOrderID
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JPackageName
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JProductID
),
PurchaseTime
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JPurchaseToken
),
PurchaseState
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JDeveloperPayload
),
bAcknowledged
,
bAutoRenewing
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JOriginalJSON
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JSignature
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JObfuscatedAccountID
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JObfuscatedProfileID
)
};
}
FPurchaseRecordFromHistory
GetPurchaseRecordFromHistoryStructFromJavaObject
(
JNIEnv
*
Env
,
jobject
Object
)
{
if
(
Object
==
NULL
||
Env
==
NULL
)
{
return
{};
}
// PurchaseRecordFromHistory class fields
auto
JProductID
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecordFromHistory_ProductID_FieldID
);
int64
PurchaseTime
=
(
int64
)
Env
->
GetLongField
(
Object
,
PurchaseRecordFromHistory_PurchaseTime_FieldID
);
auto
JPurchaseToken
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecordFromHistory_PurchaseToken_FieldID
);
auto
JDeveloperPayload
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecordFromHistory_DeveloperPayload_FieldID
);
auto
JOriginalJSON
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecordFromHistory_OriginalJSON_FieldID
);
auto
JSignature
=
GetScopedStringFromObject
(
Env
,
Object
,
PurchaseRecordFromHistory_Signature_FieldID
);
// Construct FPurchaseRecordFromHistory struct
return
{
FJavaHelper
::
FStringFromParam
(
Env
,
*
JProductID
),
PurchaseTime
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JPurchaseToken
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JDeveloperPayload
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JOriginalJSON
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JSignature
),
};
}
FSkuDetailsRecord
GetSkuDetailsRecordStructFromJavaObject
(
JNIEnv
*
Env
,
jobject
Object
)
{
if
(
Object
==
NULL
||
Env
==
NULL
)
{
return
{};
}
// SkuDetailsRecord class fields
auto
JOriginalJSON
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_OriginalJSON_FieldID
);
auto
JProductID
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_ProductID_FieldID
);
auto
JType
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_Type_FieldID
);
auto
JPrice
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_Price_FieldID
);
int64
PriceAmountMicros
=
(
int64
)
Env
->
GetLongField
(
Object
,
SkuDetailsRecord_PriceAmountMicros_FieldID
);
auto
JPriceCurrencyCode
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_PriceCurrencyCode_FieldID
);
auto
JOriginalPrice
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_OriginalPrice_FieldID
);
int64
OriginalPriceAmountMicros
=
(
int64
)
Env
->
GetLongField
(
Object
,
SkuDetailsRecord_OriginalPriceAmountMicros_FieldID
);
auto
JTitle
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_Title_FieldID
);
auto
JDescription
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_Description_FieldID
);
auto
JSubscriptionPeriod
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_SubscriptionPeriod_FieldID
);
auto
JFreeTrialPeriod
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_FreeTrialPeriod_FieldID
);
auto
JIntroductoryPrice
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_IntroductoryPrice_FieldID
);
int64
IntroductoryPriceAmountMicros
=
(
int64
)
Env
->
GetLongField
(
Object
,
SkuDetailsRecord_IntroductoryPriceAmountMicros_FieldID
);
auto
JIntroductoryPricePeriod
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_IntroductoryPricePeriod_FieldID
);
int
IntroductoryPriceCycles
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_IntroductoryPriceCycles_FieldID
);
auto
JIconURL
=
GetScopedStringFromObject
(
Env
,
Object
,
SkuDetailsRecord_IconURL_FieldID
);
// Construct FSkuDetailsRecord struct
return
{
FJavaHelper
::
FStringFromParam
(
Env
,
*
JOriginalJSON
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JProductID
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JType
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JPrice
),
PriceAmountMicros
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JPriceCurrencyCode
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JOriginalPrice
),
OriginalPriceAmountMicros
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JTitle
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JDescription
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JSubscriptionPeriod
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JFreeTrialPeriod
),
FJavaHelper
::
FStringFromParam
(
Env
,
*
JIntroductoryPrice
),
IntroductoryPriceAmountMicros
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JIntroductoryPricePeriod
),
IntroductoryPriceCycles
,
FJavaHelper
::
FStringFromParam
(
Env
,
*
JIconURL
),
};
}
#endif
void
UAndroidPlayBillingSubsystem
::
LaunchBillingFlow
(
FOnLaunchBillingFlowCompleted
OnOperationResult
,
const
FString
&
ProductID
,
const
ESkuType
ProductType
)
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Bind callback
Module
->
AddOnLaunchBillingFlowCompleted_Handle
(
OnOperationResult
);
auto
JProductID
=
FJavaHelper
::
ToJavaString
(
Env
,
ProductID
);
// Call Java method
CallVoidMethod
(
Env
,
LaunchBillingFlow_MethodID
,
*
JProductID
,
(
int
)
ProductType
);
}
}
#endif
}
void
UAndroidPlayBillingSubsystem
::
LaunchPriceChangeConfirmationFlow
(
FOnOperationCompleted
OnOperationResult
,
const
FString
&
ProductID
,
const
ESkuType
ProductType
)
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Bind callback
Module
->
AddOnOperationCompleted_Handle
(
OnOperationResult
);
auto
JProductID
=
FJavaHelper
::
ToJavaString
(
Env
,
ProductID
);
// Call Java method
CallVoidMethod
(
Env
,
LaunchPriceChangeConfirmationFlow_MethodID
,
*
JProductID
,
(
int
)
ProductType
);
}
}
#endif
}
void
UAndroidPlayBillingSubsystem
::
QueryPurchases
(
FOnQueryPurchasesCompleted
OnOperationResult
,
const
ESkuType
ProductType
)
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Bind callback
Module
->
AddOnQueryPurchasesCompleted_Handle
(
OnOperationResult
);
// Call Java method
CallVoidMethod
(
Env
,
QueryPurchases_MethodID
,
(
int
)
ProductType
);
}
}
#endif
}
void
UAndroidPlayBillingSubsystem
::
QuerySkuDetails
(
FOnQuerySkuDetailsCompleted
OnOperationResult
,
const
TArray
<
FString
>&
ProductIDs
,
const
ESkuType
ProductType
)
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Bind callback
Module
->
AddOnQuerySkuDetailsCompleted_Handle
(
OnOperationResult
);
// Populate some java types with the provided product information
auto
JProductIDs
=
NewScopedJavaObject
(
Env
,
(
jobjectArray
)
Env
->
NewObjectArray
(
ProductIDs
.
Num
(),
FJavaWrapper
::
JavaStringClass
,
NULL
));
for
(
int
Idx
=
0
;
Idx
<
ProductIDs
.
Num
();
Idx
++
)
{
auto
JProductID
=
FJavaHelper
::
ToJavaString
(
Env
,
ProductIDs
[
Idx
]);
Env
->
SetObjectArrayElement
(
*
JProductIDs
,
Idx
,
*
JProductID
);
}
// Call Java method
CallVoidMethod
(
Env
,
QuerySkuDetails_MethodID
,
*
JProductIDs
,
(
int
)
ProductType
);
}
}
#endif
}
void
UAndroidPlayBillingSubsystem
::
QueryPurchaseHistory
(
FOnQueryPurchaseHistoryCompleted
OnOperationResult
,
const
ESkuType
ProductType
)
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Bind callback
Module
->
AddOnQueryPurchaseHistoryCompleted_Handle
(
OnOperationResult
);
// Call Java method
CallVoidMethod
(
Env
,
QueryPurchaseHistory_MethodID
,
(
int
)
ProductType
);
}
}
#endif
}
EBillingResponseCode
UAndroidPlayBillingSubsystem
::
IsFeatureSupported
(
const
EFeatureType
FeatureType
)
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
// Call Java method
return
(
EBillingResponseCode
)
CallIntMethod
(
Env
,
IsFeatureSupported_MethodID
,
(
int
)
FeatureType
);
}
#endif
return
EBillingResponseCode
::
SERVICE_UNAVAILABLE
;
}
bool
UAndroidPlayBillingSubsystem
::
IsIAPInitialized
()
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
// Call Java method
return
CallBooleanMethod
(
Env
,
IsIAPInitialized_MethodID
);
}
#endif
return
false
;
}
bool
UAndroidPlayBillingSubsystem
::
IsPurchased
(
const
FString
&
ProductID
)
{
#if PLATFORM_ANDROID
if
(
JNIEnv
*
Env
=
FAndroidApplication
::
GetJavaEnv
())
{
auto
JProductID
=
FJavaHelper
::
ToJavaString
(
Env
,
ProductID
);
// Call Java method
return
CallBooleanMethod
(
Env
,
IsIAPInitialized_MethodID
,
*
JProductID
);
}
#endif
return
false
;
}
#if PLATFORM_ANDROID
JNI_METHOD
void
Java_com_kulichin_androidplaybilling_AndroidPlayBilling_NativeInitialize
(
JNIEnv
*
Env
,
jobject
Thiz
,
jclass
SkuDetailsRecordClass
,
jclass
PurchaseRecordFromHistoryClass
,
jclass
PurchaseRecordClass
)
{
// SkuDetailsRecord class fields
SkuDetailsRecord_OriginalJSON_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"OriginalJSON"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_ProductID_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"ProductID"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_Type_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"Type"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_Price_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"Price"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_PriceAmountMicros_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"PriceAmountMicros"
,
"J"
);
SkuDetailsRecord_PriceCurrencyCode_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"PriceCurrencyCode"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_OriginalPrice_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"OriginalPrice"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_OriginalPriceAmountMicros_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"OriginalPriceAmountMicros"
,
"J"
);
SkuDetailsRecord_Title_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"Title"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_Description_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"Description"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_SubscriptionPeriod_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"SubscriptionPeriod"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_FreeTrialPeriod_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"FreeTrialPeriod"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_IntroductoryPrice_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"IntroductoryPrice"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_IntroductoryPriceAmountMicros_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"IntroductoryPriceAmountMicros"
,
"J"
);
SkuDetailsRecord_IntroductoryPricePeriod_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"IntroductoryPricePeriod"
,
"Ljava/lang/String;"
);
SkuDetailsRecord_IntroductoryPriceCycles_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"IntroductoryPriceCycles"
,
"I"
);
SkuDetailsRecord_IconURL_FieldID
=
FindField
(
Env
,
SkuDetailsRecordClass
,
"IconURL"
,
"Ljava/lang/String;"
);
// PurchaseRecordFromHistory class fields
PurchaseRecordFromHistory_ProductID_FieldID
=
FindField
(
Env
,
PurchaseRecordFromHistoryClass
,
"ProductID"
,
"Ljava/lang/String;"
);
PurchaseRecordFromHistory_PurchaseTime_FieldID
=
FindField
(
Env
,
PurchaseRecordFromHistoryClass
,
"PurchaseTime"
,
"J"
);
PurchaseRecordFromHistory_PurchaseToken_FieldID
=
FindField
(
Env
,
PurchaseRecordFromHistoryClass
,
"PurchaseToken"
,
"Ljava/lang/String;"
);
PurchaseRecordFromHistory_DeveloperPayload_FieldID
=
FindField
(
Env
,
PurchaseRecordFromHistoryClass
,
"DeveloperPayload"
,
"Ljava/lang/String;"
);
PurchaseRecordFromHistory_OriginalJSON_FieldID
=
FindField
(
Env
,
PurchaseRecordFromHistoryClass
,
"OriginalJSON"
,
"Ljava/lang/String;"
);
PurchaseRecordFromHistory_Signature_FieldID
=
FindField
(
Env
,
PurchaseRecordFromHistoryClass
,
"Signature"
,
"Ljava/lang/String;"
);
// PurchaseRecord class fields
PurchaseRecord_OrderID_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"OrderID"
,
"Ljava/lang/String;"
);
PurchaseRecord_PackageName_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"PackageName"
,
"Ljava/lang/String;"
);
PurchaseRecord_ProductID_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"ProductID"
,
"Ljava/lang/String;"
);
PurchaseRecord_PurchaseTime_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"PurchaseTime"
,
"J"
);
PurchaseRecord_PurchaseToken_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"PurchaseToken"
,
"Ljava/lang/String;"
);
PurchaseRecord_PurchaseState_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"PurchaseState"
,
"I"
);
PurchaseRecord_DeveloperPayload_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"DeveloperPayload"
,
"Ljava/lang/String;"
);
PurchaseRecord_bAcknowledged_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"bAcknowledged"
,
"Z"
);
PurchaseRecord_bAutoRenewing_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"bAutoRenewing"
,
"Z"
);
PurchaseRecord_OriginalJSON_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"OriginalJSON"
,
"Ljava/lang/String;"
);
PurchaseRecord_Signature_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"Signature"
,
"Ljava/lang/String;"
);
PurchaseRecord_ObfuscatedAccountID_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"ObfuscatedAccountID"
,
"Ljava/lang/String;"
);
PurchaseRecord_ObfuscatedProfileID_FieldID
=
FindField
(
Env
,
PurchaseRecordClass
,
"ObfuscatedProfileID"
,
"Ljava/lang/String;"
);
// Find Google Play Billing methods
LaunchBillingFlow_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_LaunchBillingFlow"
,
"(Ljava/lang/String;I)V"
);
LaunchPriceChangeConfirmationFlow_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_LaunchPriceChangeConfirmationFlow"
,
"(Ljava/lang/String;I)V"
);
QueryPurchases_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_QueryPurchases"
,
"(I)V"
);
QuerySkuDetails_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_QuerySkuDetails"
,
"([Ljava/lang/String;I)V"
);
QueryPurchaseHistory_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_QueryPurchaseHistory"
,
"(I)V"
);
IsFeatureSupported_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_IsFeatureSupported"
,
"(I)I"
);
IsIAPInitialized_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_IsIAPInitialized"
,
"()Z"
);
IsPurchased_MethodID
=
FindMethod
(
Env
,
"AndroidThunkJava_IsPurchased"
,
"(Ljava/lang/String;)Z"
);
}
JNI_METHOD
void
Java_com_kulichin_androidplaybilling_AndroidPlayBilling_NativeCompleteOperation
(
JNIEnv
*
Env
,
jobject
Thiz
,
jint
ResponseCode
,
jstring
ResponseMessage
)
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
Module
->
TriggerOnOperationCompleted
(
(
EBillingResponseCode
)
ResponseCode
,
FJavaHelper
::
FStringFromParam
(
Env
,
ResponseMessage
)
);
}
}
JNI_METHOD
void
Java_com_kulichin_androidplaybilling_AndroidPlayBilling_NativeCompleteLaunchBillingFlow
(
JNIEnv
*
Env
,
jobject
Thiz
,
jint
ResponseCode
,
jstring
ResponseMessage
,
jobjectArray
Array
,
jint
ArraySize
)
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Convert java record
TArray
<
FPurchaseRecord
>
PurchaseRecords
=
{};
for
(
int
Idx
=
0
;
Idx
<
ArraySize
;
Idx
++
)
{
// Get element from java array
auto
JPurchaseRecord
=
NewScopedJavaObject
(
Env
,
Env
->
GetObjectArrayElement
(
Array
,
Idx
));
// Construct struct & add to array
PurchaseRecords
.
Add
(
GetPurchaseRecordStructFromJavaObject
(
Env
,
*
JPurchaseRecord
));
}
Module
->
TriggerOnLaunchBillingFlowCompleted
(
(
EBillingResponseCode
)
ResponseCode
,
FJavaHelper
::
FStringFromParam
(
Env
,
ResponseMessage
),
PurchaseRecords
);
}
}
JNI_METHOD
void
Java_com_kulichin_androidplaybilling_AndroidPlayBilling_NativeCompleteQueryPurchaseHistory
(
JNIEnv
*
Env
,
jobject
Thiz
,
jint
ResponseCode
,
jstring
ResponseMessage
,
jobjectArray
Array
,
jint
ArraySize
)
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Convert java record
TArray
<
FPurchaseRecordFromHistory
>
PurchaseRecordsFromHistory
=
{};
for
(
int
Idx
=
0
;
Idx
<
ArraySize
;
Idx
++
)
{
// Get element from java array
auto
JRecordFromHistory
=
NewScopedJavaObject
(
Env
,
Env
->
GetObjectArrayElement
(
Array
,
Idx
));
// Construct struct & add to array
PurchaseRecordsFromHistory
.
Add
(
GetPurchaseRecordFromHistoryStructFromJavaObject
(
Env
,
*
JRecordFromHistory
));
}
Module
->
TriggerOnQueryPurchaseHistoryCompleted
(
(
EBillingResponseCode
)
ResponseCode
,
FJavaHelper
::
FStringFromParam
(
Env
,
ResponseMessage
),
PurchaseRecordsFromHistory
);
}
}
JNI_METHOD
void
Java_com_kulichin_androidplaybilling_AndroidPlayBilling_NativeCompleteQuerySkuDetails
(
JNIEnv
*
Env
,
jobject
Thiz
,
jint
ResponseCode
,
jstring
ResponseMessage
,
jobjectArray
Array
,
jint
ArraySize
)
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Convert java record
TArray
<
FSkuDetailsRecord
>
SkuDetailsRecords
=
{};
for
(
int
Idx
=
0
;
Idx
<
ArraySize
;
Idx
++
)
{
// Get element from java array
auto
JSkuDetailsRecord
=
NewScopedJavaObject
(
Env
,
Env
->
GetObjectArrayElement
(
Array
,
Idx
));
// Construct struct & add to array
SkuDetailsRecords
.
Add
(
GetSkuDetailsRecordStructFromJavaObject
(
Env
,
*
JSkuDetailsRecord
));
}
Module
->
TriggerOnQuerySkuDetailsCompleted
(
(
EBillingResponseCode
)
ResponseCode
,
FJavaHelper
::
FStringFromParam
(
Env
,
ResponseMessage
),
SkuDetailsRecords
);
}
}
JNI_METHOD
void
Java_com_kulichin_androidplaybilling_AndroidPlayBilling_NativeCompleteQueryPurchases
(
JNIEnv
*
Env
,
jobject
Thiz
,
jint
ResponseCode
,
jstring
ResponseMessage
,
jobjectArray
Array
,
jint
ArraySize
)
{
if
(
FAndroidPlayBillingModule
*
Module
=
FAndroidPlayBillingModule
::
GetModule
())
{
// Convert java record
TArray
<
FPurchaseRecord
>
PurchaseRecords
=
{};
for
(
int
Idx
=
0
;
Idx
<
ArraySize
;
Idx
++
)
{
// Get element from java array
auto
JPurchaseRecord
=
NewScopedJavaObject
(
Env
,
Env
->
GetObjectArrayElement
(
Array
,
Idx
));
// Construct struct & add to array
PurchaseRecords
.
Add
(
GetPurchaseRecordStructFromJavaObject
(
Env
,
*
JPurchaseRecord
));
}
Module
->
TriggerOnQueryPurchasesCompleted
(
(
EBillingResponseCode
)
ResponseCode
,
FJavaHelper
::
FStringFromParam
(
Env
,
ResponseMessage
),
PurchaseRecords
);
}
}
#endif
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/Public/AndroidPlayBilling.h
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
#pragma once
#include "AndroidPlayBillingSubsystem.h"
#include "Engine.h"
#define INITIALIZE_DELEGATE_HANDLER_Base(DelegateClass, DelegateName) \
public: \
DelegateClass DelegateName; \
void Add##DelegateName##_Handle(const DelegateClass& Delegate) \
{ \
ClearAll##DelegateName(); \
DelegateName = Delegate; \
} \
void ClearAll##DelegateName() \
{ \
if(DelegateName.IsBound()) \
{ \
DelegateName.Clear(); \
} \
}
#define INITIALIZE_DELEGATE_HANDLER_TwoParams( \
DelegateClass, DelegateName, \
FirstParamType, FirstParamName, SecondParamType, SecondParamName) \
INITIALIZE_DELEGATE_HANDLER_Base(DelegateClass, DelegateName) \
void Trigger##DelegateName(FirstParamType FirstParamName, SecondParamType SecondParamName) \
{ \
if (DelegateName.IsBound()) \
{ \
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady( \
FSimpleDelegateGraphTask::FDelegate::CreateLambda([FirstParamName, SecondParamName, this]() \
{ \
DelegateName.Execute(FirstParamName, SecondParamName); \
ClearAll##DelegateName(); \
}), \
TStatId(), nullptr, ENamedThreads::GameThread); \
} \
}
#define INITIALIZE_DELEGATE_HANDLER_ThreeParams( \
DelegateClass, DelegateName, \
FirstParamType, FirstParamName, SecondParamType, SecondParamName, ThirdParamType, ThirdParamName) \
INITIALIZE_DELEGATE_HANDLER_Base(DelegateClass, DelegateName) \
void Trigger##DelegateName(FirstParamType FirstParamName, SecondParamType SecondParamName, ThirdParamType ThirdParamName) \
{ \
if (DelegateName.IsBound()) \
{ \
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady( \
FSimpleDelegateGraphTask::FDelegate::CreateLambda([FirstParamName, SecondParamName, ThirdParamName, this]() \
{ \
DelegateName.Execute(FirstParamName, SecondParamName, ThirdParamName); \
ClearAll##DelegateName(); \
}), \
TStatId(), nullptr, ENamedThreads::GameThread); \
} \
}
class
FAndroidPlayBillingModule
:
public
IModuleInterface
{
public
:
virtual
void
StartupModule
()
override
;
virtual
void
ShutdownModule
()
override
;
static
FAndroidPlayBillingModule
*
GetModule
();
#if PLATFORM_ANDROID
INITIALIZE_DELEGATE_HANDLER_TwoParams
(
FOnOperationCompleted
,
OnOperationCompleted
,
const
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
)
INITIALIZE_DELEGATE_HANDLER_ThreeParams
(
FOnLaunchBillingFlowCompleted
,
OnLaunchBillingFlowCompleted
,
const
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FPurchaseRecord
>&
,
UpdatedPurchaseRecords
);
INITIALIZE_DELEGATE_HANDLER_ThreeParams
(
FOnQueryPurchaseHistoryCompleted
,
OnQueryPurchaseHistoryCompleted
,
const
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FPurchaseRecordFromHistory
>&
,
PurchaseRecordsFromHistory
);
INITIALIZE_DELEGATE_HANDLER_ThreeParams
(
FOnQuerySkuDetailsCompleted
,
OnQuerySkuDetailsCompleted
,
const
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FSkuDetailsRecord
>&
,
SkuDetailsRecords
);
INITIALIZE_DELEGATE_HANDLER_ThreeParams
(
FOnQueryPurchasesCompleted
,
OnQueryPurchasesCompleted
,
const
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FPurchaseRecord
>&
,
PurchaseRecords
);
#endif
};
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/Public/AndroidPlayBillingSettings.h
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "AndroidPlayBillingSettings.generated.h"
UCLASS
(
transient
,
config
=
Engine
)
class
UAndroidPlayBillingSettings
:
public
UObject
{
GENERATED_BODY
()
UPROPERTY
(
Config
,
EditAnywhere
,
Category
=
"Android Play Billing"
)
FString
BillingLicenseKey
;
#if WITH_EDITOR
virtual
void
PostEditChangeProperty
(
struct
FPropertyChangedEvent
&
PropertyChangedEvent
)
override
;
#endif
};
Plugins/AndroidPlayBilling/Source/AndroidPlayBilling/Public/AndroidPlayBillingSubsystem.h
0 → 100644
View file @
56a3b326
// Copyright (C) 2021. Nikita Klimov. All rights reserved.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "AndroidPlayBillingSubsystem.generated.h"
UENUM
(
BlueprintType
)
enum
EBillingResponseCode
{
/**
* Billing API version is not supported for the type requested.
*/
BILLING_UNAVAILABLE
=
3
UMETA
(
DisplayName
=
"BillingUnavailable"
),
/**
* Invalid arguments provided to the API.
*/
DEVELOPER_ERROR
=
5
UMETA
(
DisplayName
=
"DeveloperError"
),
/**
* Fatal error during the API action.
*/
ERROR
=
6
UMETA
(
DisplayName
=
"Error"
),
/**
* Requested feature is not supported by Play Store on the
* current device.
*/
FEATURE_NOT_SUPPORTED
=
-
2
UMETA
(
DisplayName
=
"FeatureNotSupported"
),
/**
* Failure to purchase since item is already owned.
*/
ITEM_ALREADY_OWNED
=
7
UMETA
(
DisplayName
=
"ItemAlreadyOwned"
),
/**
* Failure to consume since item is not owned.
*/
ITEM_NOT_OWNED
=
8
UMETA
(
DisplayName
=
"ItemNotOwned"
),
/**
* Requested product is not available for purchase.
*/
ITEM_UNAVAILABLE
=
4
UMETA
(
DisplayName
=
"ItemUnavailable"
),
/**
* Success.
*/
OK
=
0
UMETA
(
DisplayName
=
"OK"
),
/**
* Play Store service is not connected now - potentially
* transient state.
*/
SERVICE_DISCONNECTED
=
-
1
UMETA
(
DisplayName
=
"ServiceDisconnected"
),
/**
* The request has reached the maximum timeout before
* Google Play responds.
*/
SERVICE_TIMEOUT
=
-
3
UMETA
(
DisplayName
=
"ServiceTimeout"
),
/**
* Network connection is down.
*/
SERVICE_UNAVAILABLE
=
2
UMETA
(
DisplayName
=
"ServiceUnavailable"
),
/**
* User pressed back or canceled a dialog.
*/
USER_CANCELED
=
1
UMETA
(
DisplayName
=
"UserCanceled"
)
};
UENUM
(
BlueprintType
)
enum
EFeatureType
{
/**
* Purchase/query for in-app items on VR.
*/
IN_APP_ITEMS_ON_VR
=
0
UMETA
(
DisplayName
=
"InAppPurchaseOnVR"
),
/**
* Launch a price change confirmation flow.
*/
PRICE_CHANGE_CONFIRMATION
=
1
UMETA
(
DisplayName
=
"PriceChangeConfirmation"
),
/**
* Purchase/query for subscriptions.
*/
SUBSCRIPTIONS
=
2
UMETA
(
DisplayName
=
"Subcriptions"
),
/**
* Purchase/query for subscriptions on VR.
*/
SUBSCRIPTIONS_ON_VR
=
3
UMETA
(
DisplayName
=
"SubscriptionsOnVR"
),
/**
* Subscriptions update/replace.
*/
SUBSCRIPTIONS_UPDATE
=
4
UMETA
(
DisplayName
=
"SubscriptionsUpdate"
)
};
UENUM
(
BlueprintType
)
enum
EPurchaseState
{
PENDING
=
2
UMETA
(
DisplayName
=
"Pending"
),
PURCHASED
=
1
UMETA
(
DisplayName
=
"Purchased"
),
UNSPECIFIED_STATE
=
0
UMETA
(
DisplayName
=
"UnspecifiedState"
)
};
UENUM
(
BlueprintType
)
enum
ESkuType
{
/**
* A type of SKU for Android apps in-app products.
*/
INAPP
=
0
UMETA
(
DisplayName
=
"InAppProduct"
),
/**
* A type of SKU for Android apps subscriptions.
*/
SUBS
=
1
UMETA
(
DisplayName
=
"InAppSubscription"
)
};
USTRUCT
(
BlueprintType
)
struct
FSkuDetailsRecord
{
GENERATED_BODY
()
/**
* Returns a String in JSON format that contains SKU details.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
OriginalJSON
;
/**
* Returns the product Id.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
ProductID
;
/**
* Returns the of the product.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
Type
;
/**
* Returns formatted price of the item, including its currency sign.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
Price
;
/**
* Returns price in micro-units, where 1,000,000 micro-units equal
* one unit of the currency.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
int64
PriceAmountMicros
;
/**
* Returns ISO 4217 currency code for price and original price.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
PriceCurrencyCode
;
/**
* Returns formatted original price of the item, including its
* currency sign.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
OriginalPrice
;
/**
* Returns the original price in micro-units, where 1,000,000
* micro-units equal one unit of the currency.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
int64
OriginalPriceAmountMicros
;
/**
* Returns the title of the product being sold.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
Title
;
/**
* Returns the description of the product.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
Description
;
/**
* Subscription period, specified in ISO 8601 format.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
SubscriptionPeriod
;
/**
* Trial period configured in Google Play Console, specified in
* ISO 8601 format.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
FreeTrialPeriod
;
/**
* Formatted introductory price of a subscription, including its
* currency sign, such as €3.99.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
IntroductoryPrice
;
/**
* Introductory price in micro-units.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
int64
IntroductoryPriceAmountMicros
;
/**
* The billing period of the introductory price, specified in
* ISO 8601 format.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
IntroductoryPricePeriod
;
/**
* The number of subscription billing periods for which the user will
* be given the introductory price, such as 3.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
int
IntroductoryPriceCycles
;
/**
* Returns the icon of the product if present.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"SkuDetailsRecord"
)
FString
IconURL
;
};
USTRUCT
(
BlueprintType
)
struct
FPurchaseRecordFromHistory
{
GENERATED_BODY
()
/**
* Returns the product Id.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecordFromHistory"
)
FString
ProductID
;
/**
* Returns the time the product was purchased, in milliseconds since
* the epoch (Jan 1, 1970).
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecordFromHistory"
)
int64
PurchaseTime
;
/**
* Returns a token that uniquely identifies a purchase for a given
* item and user pair.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecordFromHistory"
)
FString
PurchaseToken
;
/**
* Returns the payload specified when the purchase was acknowledged or
* consumed.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecordFromHistory"
)
FString
DeveloperPayload
;
/**
* Returns a String in JSON format that contains details about the
* purchase order.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecordFromHistory"
)
FString
OriginalJSON
;
/**
* Returns String containing the signature of the purchase data
* that was signed with the private key of the developer.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecordFromHistory"
)
FString
Signature
;
};
USTRUCT
(
BlueprintType
)
struct
FPurchaseRecord
{
GENERATED_BODY
()
/**
* Returns a unique order identifier for the transaction.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
OrderID
;
/**
* Returns the application package from which the purchase originated.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
PackageName
;
/**
* Returns the product Id.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
ProductID
;
/**
* Returns the time the product was purchased, in milliseconds since
* the epoch (Jan 1, 1970).
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
int64
PurchaseTime
;
/**
* Returns a token that uniquely identifies a purchase for a given
* item and user pair.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
PurchaseToken
;
/**
* Returns PurchaseState indicating the state of the purchase.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
TEnumAsByte
<
EPurchaseState
>
PurchaseState
;
/**
* Returns the payload specified when the purchase was acknowledged or
* consumed.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
DeveloperPayload
;
/**
* Indicates whether the purchase has been acknowledged.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
bool
bAcknowledged
;
/**
* Indicates whether the subscription renews automatically.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
bool
bAutoRenewing
;
/**
* Returns a String in JSON format that contains details about the
* purchase order.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
OriginalJSON
;
/**
* Returns String containing the signature of the purchase data that
* was signed with the private key of the developer.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
Signature
;
/**
* The obfuscated account id.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
ObfuscatedAccountID
;
/**
* The obfuscated profile id.
*/
UPROPERTY
(
BlueprintReadOnly
,
Category
=
"PurchaseRecord"
)
FString
ObfuscatedProfileID
;
};
/**
* Operation delegate
*/
DECLARE_DYNAMIC_DELEGATE_TwoParams
(
FOnOperationCompleted
,
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
);
/**
* Launch Billing Flow delegate
*/
DECLARE_DYNAMIC_DELEGATE_ThreeParams
(
FOnLaunchBillingFlowCompleted
,
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FPurchaseRecord
>&
,
UpdatedPurchaseRecords
);
/**
* Query Purchase History delegate
*/
DECLARE_DYNAMIC_DELEGATE_ThreeParams
(
FOnQueryPurchaseHistoryCompleted
,
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FPurchaseRecordFromHistory
>&
,
PurchaseRecordsFromHistory
);
/**
* Query Sku Details delegate
*/
DECLARE_DYNAMIC_DELEGATE_ThreeParams
(
FOnQuerySkuDetailsCompleted
,
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FSkuDetailsRecord
>&
,
SkuDetailsRecords
);
/**
* Query Purchase delegate
*/
DECLARE_DYNAMIC_DELEGATE_ThreeParams
(
FOnQueryPurchasesCompleted
,
EBillingResponseCode
,
ResponseCode
,
const
FString
&
,
ResponseMessage
,
const
TArray
<
FPurchaseRecord
>&
,
PurchaseRecords
);
// TODO: add examples
UCLASS
()
class
UAndroidPlayBillingSubsystem
:
public
UGameInstanceSubsystem
{
GENERATED_BODY
()
public
:
/**
* Initiates the billing flow for an in-app purchase or subscription.
*
* @param OnOperationResult callback is called when operation has been completed.
* @param ProductID specifies the ID of the item being purchased.
* @param ProductType specifies the Type of the item being purchased.
*/
UFUNCTION
(
BlueprintCallable
,
Category
=
"AndroidPlayBilling"
)
static
void
LaunchBillingFlow
(
FOnLaunchBillingFlowCompleted
OnOperationResult
,
const
FString
&
ProductID
,
const
ESkuType
ProductType
);
/**
* Initiates a flow to confirm the change of price for an item
* subscribed by the user.
*
* @param OnOperationResult callback is called when operation has been completed.
* @param ProductID specifies the ID of the item that has the pending price change.
* @param ProductType specifies the Type of the item that has the pending price change.
*/
UFUNCTION
(
BlueprintCallable
,
Category
=
"AndroidPlayBilling"
)
static
void
LaunchPriceChangeConfirmationFlow
(
FOnOperationCompleted
OnOperationResult
,
const
FString
&
ProductID
,
const
ESkuType
ProductType
);
/**
* Returns purchases details for currently owned items bought
* within your app.
*
* @param OnOperationResult callback is called when operation has been completed.
* @param ProductType the type of the product.
*/
UFUNCTION
(
BlueprintCallable
,
Category
=
"AndroidPlayBilling"
)
static
void
QueryPurchases
(
FOnQueryPurchasesCompleted
OnOperationResult
,
const
ESkuType
ProductType
);
/**
* Performs a network query to get SKU details and return the
* result asynchronously.
*
* @param OnOperationResult callback is called when operation has been completed.
* @param ProductIDs specifies the ProductIDs that are queried for as
* published in the Google Developer console.
*
* @param ProductType specifies the Type of the product to query.
*/
UFUNCTION
(
BlueprintCallable
,
Category
=
"AndroidPlayBilling"
)
static
void
QuerySkuDetails
(
FOnQuerySkuDetailsCompleted
OnOperationResult
,
const
TArray
<
FString
>&
ProductIDs
,
const
ESkuType
ProductType
);
/**
* Returns the most recent purchase made by the user for each SKU,
* even if that purchase is expired, canceled, or consumed.
*
* @param OnOperationResult callback is called when operation has been completed.
* @param ProductType the type of the product.
*/
UFUNCTION
(
BlueprintCallable
,
Category
=
"AndroidPlayBilling"
)
static
void
QueryPurchaseHistory
(
FOnQueryPurchaseHistoryCompleted
OnOperationResult
,
const
ESkuType
ProductType
);
/**
* Checks if the specified feature or capability is supported
* by the Play Store.
*
* @param FeatureType one of the EFeatureType constants.
*/
UFUNCTION
(
BlueprintPure
,
Category
=
"AndroidPlayBilling"
)
static
EBillingResponseCode
IsFeatureSupported
(
const
EFeatureType
FeatureType
);
/**
* Checks if the client is currently connected to the service, so
* that requests to other methods will succeed.
*/
UFUNCTION
(
BlueprintPure
,
Category
=
"AndroidPlayBilling"
)
static
bool
IsIAPInitialized
();
/**
* Checks if user owning the product.
*
* @param ProductID specifies the ID of the item.
*/
UFUNCTION
(
BlueprintPure
,
Category
=
"AndroidPlayBilling"
)
static
bool
IsPurchased
(
const
FString
&
ProductID
);
};
Plugins/HuaweiSDK.zip
0 → 100644
View file @
56a3b326
File added
Plugins/HuaweiSDK/Source/HuaweiSDK/HuaweiSDK_UPL.xml
View file @
56a3b326
...
...
@@ -89,6 +89,15 @@
<buildGradleAdditions>
<insert>
android
{
compileOptions
{
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
apply plugin: 'com.android.application'
dependencies
...
...
@@ -139,6 +148,7 @@
import com.Plugins.HuaweiSDK.activity.ConsumptionActivity;
import com.Plugins.HuaweiSDK.activity.EntryActivity;
import com.Plugins.HuaweiSDK.activity.AccountActivity;
</insert>
</gameActivityImportAdditions>
...
...
@@ -146,11 +156,19 @@
<insert>
</insert>
</gameActivityPostImportAdditions>
<gameActivityOnCreateAdditions>
<insert>
mAccountActivity = new AccountActivity(this);
</insert>
</gameActivityOnCreateAdditions>
<gameActivityClassAdditions>
<insert>
private ConsumptionActivity mConsumptionActivity;
private EntryActivity mEntryActivity;
private AccountActivity mAccountActivity;
public void AndroidThunkJava_CreateHuaweiIap()
{
mConsumptionActivity = new ConsumptionActivity(this);
...
...
@@ -170,18 +188,19 @@
{
mEntryActivity.Payment(productId);
}
</insert>
</gameActivityClassAdditions>
<gameActivityOnCreateAdditions>
<insert>
public void AndroidThunkJava_SignIn()
{
mAccountActivity = new AccountActivity(this);
mAccountActivity.signIn();
}
</insert>
</gameActivity
OnCreate
Additions>
</gameActivity
Class
Additions>
<gameActivityOnActivityResultAdditions>
<insert>
mEntryActivity.onActivityResult(requestCode, resultCode, data);
mAccountActivity.onActivityResult(requestCode, resultCode, data);
</insert>
</gameActivityOnActivityResultAdditions>
...
...
Plugins/HuaweiSDK/Source/HuaweiSDK/Private/Android/Java/activity/AccountActivity.java
View file @
56a3b326
package
com
.
Plugins
.
HuaweiSDK
.
activity
;
import
android.app.Activity
;
import
android.content.Intent
;
import
android.app.Fragment
;
import
android.app.FragmentTransaction
;
import
android.util.Log
;
import
com.huawei.hmf.tasks.Task
;
import
com.huawei.hms.common.ApiException
;
import
com.huawei.hms.support.account.AccountAuthManager
;
import
com.huawei.hms.support.account.request.AccountAuthParams
;
import
com.huawei.hms.support.account.request.AccountAuthParamsHelper
;
import
com.huawei.hms.support.account.result.AuthAccount
;
import
com.huawei.hms.support.account.service.AccountAuthService
;
public
class
AccountActivity
extends
a
ctivity
{
public
class
AccountActivity
extends
A
ctivity
{
private
String
TAG
=
"HuaweiAccountActivity"
;
private
AccountAuthService
mAuthManager
;
...
...
@@ -20,13 +26,15 @@ public class AccountActivity extends activity {
mActivity
=
activity
;
}
private
void
signIn
()
{
public
void
signIn
()
{
Log
.
i
(
TAG
,
"AccountActivity.signIn"
);
mAuthParam
=
new
AccountAuthParamsHelper
(
AccountAuthParams
.
DEFAULT_AUTH_REQUEST_PARAM
)
.
setIdToken
()
.
setAccessToken
()
.
createParams
();
mAuthManager
=
AccountAuthManager
.
getService
(
mActivity
,
mAuthParam
);
startActivityForResult
(
mAuthManager
.
getSignInIntent
(),
Constant
.
REQUEST_SIGN_IN_LOGIN
);
startActivityForResult
(
mAuthManager
.
getSignInIntent
(),
1002
);
}
public
void
onActivityResult
(
int
requestCode
,
int
resultCode
,
Intent
data
)
{
...
...
@@ -45,10 +53,10 @@ public class AccountActivity extends activity {
}
else
{
Log
.
i
(
TAG
,
"signIn failed: "
+
((
ApiException
)
authAccountTask
.
getException
()).
getStatusCode
());
nativeSignInResult
(
false
,
0
);
nativeSignInResult
(
false
,
"0"
);
}
}
if
(
requestCode
==
Constant
.
REQUEST_SIGN_IN_LOGIN_CODE
)
{
if
(
requestCode
==
1003
)
{
//login success
Task
<
AuthAccount
>
authAccountTask
=
AccountAuthManager
.
parseAuthResultFromIntent
(
data
);
if
(
authAccountTask
.
isSuccessful
())
{
...
...
Plugins/HuaweiSDK/Source/HuaweiSDK/Private/Android/Java/activity/EntryActivity.java
View file @
56a3b326
...
...
@@ -2,7 +2,7 @@ package com.Plugins.HuaweiSDK.activity;
import
android.app.Activity
;
import
android.content.Intent
;
import
android
.support.v7
.app.AppCompatActivity
;
import
android
x.appcompat
.app.AppCompatActivity
;
import
android.text.TextUtils
;
import
android.util.Log
;
...
...
Plugins/HuaweiSDK/Source/HuaweiSDK/Private/HuaweiAccount.cpp
View file @
56a3b326
...
...
@@ -38,8 +38,13 @@ static void ReqSignIn()
static
jmethodID
SignInMethod
=
FJavaWrapper
::
FindMethod
(
Env
,
FJavaWrapper
::
GameActivityClassID
,
"AndroidThunkJava_SignIn"
,
"()V"
,
false
);
if
(
SignInMethod
)
{
UE_LOG
(
LogTemp
,
Log
,
TEXT
(
"Find AndroidThunkJava_SignIn"
));
FJavaWrapper
::
CallVoidMethod
(
Env
,
FJavaWrapper
::
GameActivityThis
,
SignInMethod
);
}
else
{
UE_LOG
(
LogTemp
,
Log
,
TEXT
(
"!Find AndroidThunkJava_SignIn"
));
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment