增加google play支付
This commit is contained in:
parent
26097c441c
commit
55d58c86e5
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
File diff suppressed because one or more lines are too long
@ -491,3 +491,20 @@ function nftMallBuy(funId, currency, addresses, ids, amounts, values, signature,
|
||||
}
|
||||
|
||||
// end of NFT mall
|
||||
|
||||
// begin of google pay
|
||||
function queryGoogleProducts(funId, productIds) {
|
||||
let ids = JSON.parse(productIds);
|
||||
console.log('queryGoogleProducts:: ' + productIds);
|
||||
promiseCb(funId, jc.wallet.paySvr.queryGoogleProducts(ids));
|
||||
}
|
||||
|
||||
function queryGooglePurchases(funId) {
|
||||
promiseCb(funId, jc.wallet.paySvr.queryGooglePurchases());
|
||||
}
|
||||
|
||||
function beginGoogleBuy(funId, productId, orderId) {
|
||||
promiseCb(funId, jc.wallet.paySvr.buyGoogleProduct(productId, orderId));
|
||||
}
|
||||
|
||||
// end of google pay
|
||||
|
@ -17,6 +17,9 @@
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="cocos2djs" />
|
||||
<meta-data
|
||||
android:name="firebase_crashlytics_collection_enabled"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="com.facebook.sdk.ApplicationId"
|
||||
android:value="@string/facebook_app_id" />
|
||||
|
@ -29,8 +29,8 @@ android {
|
||||
applicationId "com.cege.games.release"
|
||||
minSdkVersion PROP_MIN_SDK_VERSION
|
||||
targetSdkVersion PROP_TARGET_SDK_VERSION
|
||||
versionCode 12
|
||||
versionName "1.0.10"
|
||||
versionCode 46
|
||||
versionName "1.0.46"
|
||||
ndk{
|
||||
abiFilters 'armeabi-v7a','arm64-v8a'
|
||||
}
|
||||
@ -124,11 +124,11 @@ android.applicationVariants.all { variant ->
|
||||
variant.mergeAssetsProvider.get().doLast {
|
||||
def sourceDir = rootProject.ext.cfgs.jsFilePath
|
||||
|
||||
// copy{
|
||||
// from "${sourceDir}"
|
||||
// include "Data/js/**"
|
||||
// into outputDir
|
||||
// }
|
||||
copy{
|
||||
from "${sourceDir}"
|
||||
include "Data/js/**"
|
||||
into outputDir
|
||||
}
|
||||
copy {
|
||||
from "${sourceDir}/cert/cacert.pem"
|
||||
into outputDir
|
||||
@ -169,4 +169,6 @@ dependencies {
|
||||
implementation 'com.google.firebase:firebase-analytics'
|
||||
implementation 'com.google.firebase:firebase-crashlytics-ndk'
|
||||
// end of firebase
|
||||
// google pay
|
||||
implementation "com.android.billingclient:billing:6.0.1"
|
||||
}
|
@ -55,6 +55,7 @@ import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
import com.jc.jcfw.JcSDK;
|
||||
import com.jc.jcfw.appauth.AuthStateManager;
|
||||
import com.jc.jcfw.appauth.JConfiguration;
|
||||
import com.jc.jcfw.google.PayClient;
|
||||
import com.jc.jcfw.util.FileUtils;
|
||||
import com.jc.jcfw.util.JsonUtils;
|
||||
import com.king.zxing.CameraScan;
|
||||
@ -188,6 +189,9 @@ public class MainActivity extends UnityPlayerActivity
|
||||
// Uri appLinkData = appLinkIntent.getData();
|
||||
|
||||
tiktokOpenApi = TikTokOpenApiFactory.create(this);
|
||||
|
||||
PayClient payClient = PayClient.getInstance();
|
||||
payClient.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,20 +1,30 @@
|
||||
package com.jc.jcfw;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.cege.games.release.MainActivity;
|
||||
import com.jc.jcfw.google.PayClient;
|
||||
|
||||
import org.cocos2dx.lib.CocosJSHelper;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class JcSDK {
|
||||
private static final String TAG = JcSDK.class.getSimpleName();
|
||||
private static UnityCallback commonCB;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static PayClient payClient;
|
||||
|
||||
private static native int runJS(final String funId, final String methodName, final String params);
|
||||
|
||||
public static void initCommonCB(UnityCallback callBack) {
|
||||
@ -24,7 +34,7 @@ public class JcSDK {
|
||||
|
||||
/**
|
||||
* @Deprecated
|
||||
* 不使用该方法, 直接由unity调用cpp方法
|
||||
* 不使用该方法, 直接由unity调用cpp方法
|
||||
* @param password
|
||||
*/
|
||||
public static void initWallet(String password) {
|
||||
@ -35,12 +45,9 @@ public class JcSDK {
|
||||
|
||||
/**
|
||||
* 回调至c#
|
||||
*
|
||||
* @param funId
|
||||
* @param msg
|
||||
*/
|
||||
public static void csCallback(String funId, String msg) {
|
||||
if (funId != "" && funId.indexOf("js_") == 0) {
|
||||
if (!Objects.equals(funId, "") && funId.indexOf("js_") == 0) {
|
||||
commonCB.nativeCallback(funId, msg, 1);
|
||||
} else {
|
||||
commonCB.nativeCallback(funId, msg, 0);
|
||||
@ -51,8 +58,8 @@ public class JcSDK {
|
||||
* check if metamask installed and jump to metamask
|
||||
*
|
||||
* @param url
|
||||
* sample:
|
||||
* "https://metamask.app.link/wc?uri="+ExampleApplication.config.toWCUri();
|
||||
* sample:
|
||||
* "https://metamask.app.link/wc?uri="+ExampleApplication.config.toWCUri();
|
||||
*/
|
||||
|
||||
public static void toWallet(String url) {
|
||||
@ -116,7 +123,40 @@ public class JcSDK {
|
||||
MainActivity.app.logEvent(content);
|
||||
}
|
||||
|
||||
public static void nativeCb(String funId, String error, String idToken) {
|
||||
public static void queryProducts(String funid, String skuListStr) {
|
||||
Log.i(TAG, "queryProducts with: " + skuListStr);
|
||||
if (payClient == null) {
|
||||
payClient = PayClient.getInstance();
|
||||
}
|
||||
List<String> skuList = new ArrayList<>();
|
||||
if (skuListStr.contains(",")) {
|
||||
String[] skuArr = skuListStr.split(",");
|
||||
skuList.addAll(Arrays.asList(skuArr));
|
||||
} else {
|
||||
skuList.add(skuListStr);
|
||||
}
|
||||
payClient.queryProductList(funid, skuList);
|
||||
}
|
||||
public static void buyProduct(String funid, String productId, String orderId) {
|
||||
Log.i(TAG, "buyProduct with: " + productId);
|
||||
if (payClient == null) {
|
||||
payClient = PayClient.getInstance();
|
||||
}
|
||||
payClient.buyOne(funid, productId, orderId);
|
||||
}
|
||||
|
||||
public static void queryPurchase(String funid) {
|
||||
Log.i(TAG, "queryPurchase");
|
||||
if (payClient == null) {
|
||||
payClient = PayClient.getInstance();
|
||||
}
|
||||
payClient.queryPurchase(funid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调至js
|
||||
*/
|
||||
public static void nativeCb(String funId, String error, String dataStr) {
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
if (error != null && !error.isEmpty()) {
|
||||
@ -124,7 +164,7 @@ public class JcSDK {
|
||||
result.put("errmessage", error);
|
||||
} else {
|
||||
result.put("errcode", 0);
|
||||
result.put("data", idToken);
|
||||
result.put("data", dataStr);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "JSONException: " + e.getMessage());
|
||||
@ -132,7 +172,6 @@ public class JcSDK {
|
||||
if (funId == null || funId.isEmpty()) {
|
||||
funId = MainActivity.app.getFunId();
|
||||
}
|
||||
|
||||
JcSDK.runJS(funId, "jniCallback", result.toString());
|
||||
}
|
||||
}
|
||||
|
243
app/src/com/jc/jcfw/google/PayClient.java
Normal file
243
app/src/com/jc/jcfw/google/PayClient.java
Normal file
@ -0,0 +1,243 @@
|
||||
package com.jc.jcfw.google;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
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.ProductDetails;
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||
import com.android.billingclient.api.QueryProductDetailsParams;
|
||||
import com.android.billingclient.api.QueryPurchasesParams;
|
||||
import com.cege.games.release.MainActivity;
|
||||
import com.jc.jcfw.JcSDK;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PayClient extends Activity implements PurchasesUpdatedListener {
|
||||
private static final String TAG = "GooglePayClient";
|
||||
private static volatile PayClient mInstance = null;
|
||||
private static BillingClient billingClient;
|
||||
private static ConcurrentHashMap<String, ProductDetails> skuDetailsMap;
|
||||
private Context mContext = null;
|
||||
private String mFunId;
|
||||
|
||||
public static PayClient getInstance() {
|
||||
if (null == mInstance) {
|
||||
synchronized (PayClient.class) {
|
||||
if (null == mInstance) {
|
||||
mInstance = new PayClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public void init(Context context) {
|
||||
this.mContext = context;
|
||||
skuDetailsMap = new ConcurrentHashMap<>();
|
||||
billingClient = BillingClient.newBuilder(context).enablePendingPurchases().setListener(this).build();
|
||||
connectToPlay();
|
||||
}
|
||||
|
||||
private void connectToPlay() {
|
||||
billingClient.startConnection(new BillingClientStateListener() {
|
||||
@Override
|
||||
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
|
||||
Log.i(TAG, "onBillingSetupFinished, response code: " + billingResult.getResponseCode());
|
||||
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
|
||||
// The BillingClient is ready. You can query purchases here.
|
||||
Log.i(TAG, "BillingClient is ready");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBillingServiceDisconnected() {
|
||||
// Try to restart the connection on the next request to
|
||||
// Google Play by calling the startConnection() method.
|
||||
connectToPlay();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
JSONArray handlePurchase(JSONArray dataArr, Purchase purchase) throws JSONException {
|
||||
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED
|
||||
// || purchase.getPurchaseState() == Purchase.PurchaseState.PENDING
|
||||
) {
|
||||
// Acknowledge purchase and grant the item to the user
|
||||
// Grant entitlement to the user.
|
||||
Log.i(TAG, "handlePurchase:" + purchase.getOriginalJson());
|
||||
Log.i(TAG, "purchase sign:" + purchase.getSignature());
|
||||
if (!purchase.isAcknowledged() && purchase.getProducts().size() > 0) {
|
||||
// consumables 消耗型
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("id", purchase.getProducts().get(0));
|
||||
data.put("token", purchase.getPurchaseToken());
|
||||
dataArr.put(data);
|
||||
}
|
||||
}
|
||||
return dataArr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
|
||||
Log.i(TAG, "onPurchasesUpdated with status: " + billingResult.getResponseCode());
|
||||
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
|
||||
&& purchases != null) {
|
||||
final JSONArray dataArr = new JSONArray();
|
||||
boolean hasErr = false;
|
||||
for (Purchase purchase : purchases) {
|
||||
try {
|
||||
handlePurchase(dataArr, purchase);
|
||||
} catch (JSONException e) {
|
||||
hasErr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasErr) {
|
||||
purchaseUpdateCb("error parse purchase data", null);
|
||||
} else {
|
||||
purchaseUpdateCb(null, dataArr.toString());
|
||||
}
|
||||
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
|
||||
purchaseUpdateCb("user cancel buy", null);
|
||||
} else {
|
||||
String errmsg = billingResult.getDebugMessage();
|
||||
if (errmsg.isEmpty()) {
|
||||
errmsg = "other error";
|
||||
}
|
||||
purchaseUpdateCb(errmsg, null);
|
||||
}
|
||||
}
|
||||
private void purchaseUpdateCb(String error, String dataStr) {
|
||||
if (mFunId != null && !mFunId.isEmpty()) {
|
||||
JcSDK.nativeCb(mFunId, error, dataStr);
|
||||
mFunId = null;
|
||||
}
|
||||
}
|
||||
private boolean parseProductDetails(JSONArray dataArr, ProductDetails skuDetails) {
|
||||
JSONObject data = new JSONObject();
|
||||
try {
|
||||
data.put("name", skuDetails.getTitle());
|
||||
data.put("description", skuDetails.getDescription());
|
||||
data.put("id", skuDetails.getProductId());
|
||||
data.put("type", skuDetails.getProductType());
|
||||
if (skuDetails.getProductType().equals(BillingClient.ProductType.INAPP)) {
|
||||
data.put("currencyCode",
|
||||
skuDetails.getOneTimePurchaseOfferDetails().getPriceCurrencyCode());
|
||||
data.put("priceValue", skuDetails.getOneTimePurchaseOfferDetails().getPriceAmountMicros());
|
||||
data.put("priceShow", skuDetails.getOneTimePurchaseOfferDetails().getFormattedPrice());
|
||||
}
|
||||
dataArr.put(data);
|
||||
return true;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public void queryProductList(String funId, List<String> productIds) {
|
||||
List<QueryProductDetailsParams.Product> productList = new ArrayList<>();
|
||||
for (String productId : productIds) {
|
||||
productList.add(
|
||||
QueryProductDetailsParams.Product.newBuilder()
|
||||
.setProductId(productId)
|
||||
.setProductType(BillingClient.ProductType.INAPP)
|
||||
.build());
|
||||
}
|
||||
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
|
||||
.setProductList(productList)
|
||||
.build();
|
||||
billingClient.queryProductDetailsAsync(
|
||||
params,
|
||||
(billingResult, productDetailsList) -> {
|
||||
// Process the result
|
||||
Map<String, ProductDetails> pMap = new HashMap<>();
|
||||
for (ProductDetails details : productDetailsList) {
|
||||
skuDetailsMap.put(details.getProductId(), details);
|
||||
pMap.put(details.getProductId(), details);
|
||||
}
|
||||
final JSONArray dataArr = new JSONArray();
|
||||
boolean hasErr = false;
|
||||
for (Map.Entry<String, ProductDetails> entry : pMap.entrySet()) {
|
||||
ProductDetails skuDetails = entry.getValue();
|
||||
if (!parseProductDetails(dataArr, skuDetails)) {
|
||||
hasErr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasErr) {
|
||||
JcSDK.nativeCb(funId, "parse product detail json error", null);
|
||||
} else {
|
||||
JcSDK.nativeCb(funId, null, dataArr.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void queryPurchase(String funId) {
|
||||
billingClient.queryPurchasesAsync(
|
||||
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(),
|
||||
(billingResult, purchases) -> {
|
||||
// Process the result
|
||||
final JSONArray dataArr = new JSONArray();
|
||||
boolean hasErr = false;
|
||||
for (Purchase purchase : purchases) {
|
||||
try {
|
||||
handlePurchase(dataArr, purchase);
|
||||
} catch (JSONException e) {
|
||||
hasErr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasErr) {
|
||||
JcSDK.nativeCb(funId, "error parse purchase data", null);
|
||||
} else {
|
||||
JcSDK.nativeCb(funId, null, dataArr.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void buyOne(String funId, String productId, String orderId) {
|
||||
if (mFunId != null && !mFunId.isEmpty()) {
|
||||
JcSDK.nativeCb(funId, "another purchase is in progress", null);
|
||||
return;
|
||||
}
|
||||
if (!skuDetailsMap.containsKey(productId)) {
|
||||
JcSDK.nativeCb(funId, "product with : "+productId+ " not found", null);
|
||||
return;
|
||||
}
|
||||
ProductDetails productDetails = skuDetailsMap.get(productId);
|
||||
// Set the parameters for the offer that will be presented
|
||||
// in the billing flow creating separate productDetailsParamsList variable
|
||||
List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = Collections
|
||||
.singletonList(BillingFlowParams.ProductDetailsParams.newBuilder()
|
||||
.setProductDetails(productDetails)
|
||||
.build());
|
||||
|
||||
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
|
||||
.setProductDetailsParamsList(productDetailsParamsList)
|
||||
.setObfuscatedAccountId(orderId)
|
||||
.setObfuscatedProfileId(orderId)
|
||||
.build();
|
||||
|
||||
// Launch the billing flow
|
||||
this.mFunId = funId;
|
||||
MainActivity.app.runOnUiThread(() -> {
|
||||
billingClient.launchBillingFlow((Activity) mContext, billingFlowParams);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user