Google pay 国际版开发
一、官网地址(科学上网)
官方对接文档 https://developers.google.com/pay/api/android/overview
Stripe对接Google Pay https://stripe.com/docs/google-pay
Stripe验证支付 https://stripe.com/docs/payments/accept-a-payment?integration=elements
Stripe管理后台 https://dashboard.stripe.com/dashboard
二、接入流程
三、主要流程
1、服务器请求orderId
2、调用Google Pay
3、通过Stripe完成支付
4、服务器完成验证
四、主要代码实现(1、4主要是服务器流程,以下主要是2、3流程)
项目配置
build.gradle 添加
implementation 'com.google.android.gms:play-services-wallet:18.1.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.stripe:stripe-android:16.0.1'
AndroidManifest.xml 的 application添加
<meta-data
android:name="com.google.android.gms.wallet.api.enabled"
android:value="true" />
调用Google Pay
@RequiresApi(api = Build.VERSION_CODES.N)
private void payWithGoogle( double price) {
if (paymentsClient == null) {
paymentsClient = createPaymentsClient(mActivity);
}
// 主要是Google Pay一些参数设置
Optional<JSONObject> paymentDataRequestJson = createPaymentDataRequest(price);
if (!paymentDataRequestJson.isPresent()) {
return;
}
PaymentDataRequest request =
PaymentDataRequest.fromJson(paymentDataRequestJson.get().toString());
if (request != null) {
if (mActivity!=null){
mActivity.showCircle2Loading();
}
// 调起 Google Pay
AutoResolveHelper.resolveTask(
paymentsClient.loadPaymentData(request),
mActivity,
LOAD_PAYMENT_DATA_REQUEST_CODE
);
}
}
Google Pay 基本设置
@RequiresApi(api = Build.VERSION_CODES.N)
private Optional<JSONObject> createPaymentDataRequest(double priceCents) {
try {
JSONObject paymentDataRequest = getBaseRequest();
// 指定是否支持 Google Pay API 所支持的一种或多种付款方式。
paymentDataRequest.put("allowedPaymentMethods", new JSONArray().put(getCardPaymentMethod()));
// 有关根据用户是否同意交易来为交易授权的详细信息。包含总价和价格状态
paymentDataRequest.put("transactionInfo", getTransactionInfo(priceCents));
//商家信息
paymentDataRequest.put("merchantInfo", getMerchantInfo());
paymentDataRequest.put("shippingAddressRequired", false);
paymentDataRequest.put("emailRequired", false);
return Optional.of(paymentDataRequest);
} catch (JSONException e) {
return Optional.empty();
}
}
private JSONObject getCardPaymentMethod() throws JSONException {
JSONObject cardPaymentMethod = getBaseCardPaymentMethod();
// 设置stripe为付款方式
JSONObject tokenizationSpec = new GooglePayConfig(LIVE_API).getTokenizationSpecification();
cardPaymentMethod.put("tokenizationSpecification", tokenizationSpec);
return cardPaymentMethod;
}
/**
* 金钱信息
*/
private JSONObject getTransactionInfo(double price) throws JSONException {
JSONObject transactionInfo = new JSONObject();
transactionInfo.put("totalPrice", price+"");
transactionInfo.put("totalPriceStatus", "FINAL");
transactionInfo.put("countryCode", COUNTRY_CODE);
transactionInfo.put("currencyCode", CURRENCY_CODE);
transactionInfo.put("checkoutOption", "COMPLETE_IMMEDIATE_PURCHASE");
return transactionInfo;
}
/**
* 商家信息
* merchantId 商家Id
*/
private JSONObject getMerchantInfo() throws JSONException {
return new JSONObject().put("merchantName", "Guruji").put("merchantId", "填写商家ID");
}
private JSONObject getBaseRequest() throws JSONException {
return new JSONObject()
.put("apiVersion", 2)
.put("apiVersionMinor", 0);
}
Google Pay返回数据
{
"apiVersionMinor": 0,
"apiVersion": 2,
"paymentMethodData": {
"description": "中国招商银行 (CMB) •••• 5019",
"tokenizationData": {
"type": "PAYMENT_GATEWAY",
"token": "{\n \"id\": \"tok_1HcQIMBcf7rsT369XhdHf1aI\",\n \"object\": \"token\",\n \"card\": {\n \"id\": \"card_1HcQIMBcf7rsT369lDDy6PIM\",\n \"object\": \"card\",\n \"address_city\": null,\n \"address_country\": null,\n \"address_line1\": null,\n \"address_line1_check\": null,\n \"address_line2\": null,\n \"address_state\": null,\n \"address_zip\": null,\n \"address_zip_check\": null,\n \"brand\": \"Visa\",\n \"country\": \"US\",\n \"cvc_check\": null,\n \"dynamic_last4\": \"4242\",\n \"exp_month\": 10,\n \"exp_year\": 2025,\n \"funding\": \"credit\",\n \"last4\": \"5019\",\n \"metadata\": {\n },\n \"name\": \"name\",\n \"tokenization_method\": \"android_pay\"\n },\n \"client_ip\": \"173.194.101.160\",\n \"created\": 1602744638,\n \"livemode\": false,\n \"type\": \"card\",\n \"used\": false\n}\n"
},
"type": "CARD",
"info": {
"cardNetwork": "VISA",
"cardDetails": "5019"
}
}
}
数据处理,然后通过Stripe生成PaymentMethodId
public void onCheckResult(int resultCode,Intent data) {
switch (resultCode) {
case Activity.RESULT_OK: {
onGooglePayResult(data);
break;
}
case Activity.RESULT_CANCELED: {
errorShowAndRetry();
break;
}
case AutoResolveHelper.RESULT_ERROR: {
final Status status =
AutoResolveHelper.getStatusFromIntent(data);
errorShowAndRetry();
break;
}
default: {
// Do nothing.
}
}
}
private void onGooglePayResult(@NonNull Intent data) {
PaymentData paymentData = PaymentData.getFromIntent(data);
if (paymentData == null) {
errorShowAndRetry();
return;
}
try {
PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.createFromGooglePay(new JSONObject(paymentData.toJson()));
mStripe.createPaymentMethod(
paymentMethodCreateParams,
new ApiResultCallback<PaymentMethod>() {
@Override
public void onSuccess(@NonNull PaymentMethod result) {
paymentGotoStripe(result.id);
}
@Override
public void onError(@NonNull Exception e) {
errorShowAndRetry();
}
}
);
} catch (Exception e) {
errorShowAndRetry();
}
}
通过Stripe完成最后支付
/**
* 得到PaymentMethodId 去stripe 支付
*/
private void paymentGotoStripe(String id){
if ( mStripe != null && !TextUtils.isEmpty(mSecret)) {
ConfirmPaymentIntentParams confirmParams = ConfirmPaymentIntentParams.createWithPaymentMethodId(id, mSecret);
mStripe.confirmPayment(mActivity, confirmParams);
Logger.logE("live pay, click pay ---");
} else if (mStripe == null) {
ToastUtils.showSystemToast(R.string.payment_in_progress_tips);
Logger.logE("live pay, click pay --- order is null");
}
}
/**
* Google --> stripe--> CallBack
* @param requestCode
* @param data
*/
public void paymentResultCallback(int requestCode,Intent data){
mStripe.onPaymentResult(requestCode, data, new PaymentResultCallback(mActivity));
}
private final class PaymentResultCallback implements ApiResultCallback<PaymentIntentResult> {
@NonNull
private final WeakReference<PaymentActivity> activityRef;
PaymentResultCallback(@NonNull PaymentActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public void onSuccess(@NonNull PaymentIntentResult result) {
PaymentActivity activity = activityRef.get();
if (activity == null) {
return;
}
PaymentIntent paymentIntent = result.getIntent();
PaymentIntent.Status status = paymentIntent.getStatus();
if (status == PaymentIntent.Status.Succeeded) {
// 后台验证订单
PaymentNetManager.verifyStripeOrder(true,mOrderId, statusData);
} else if (status == PaymentIntent.Status.RequiresPaymentMethod) {
errorShowAndRetry();
}
Logger.logE("live pay, stripe succ, status = " + status);
}
@Override
public void onError(@NonNull Exception e) {
PaymentActivity activity = activityRef.get();
if (activity == null) {
return;
}
errorShowAndRetry();
Logger.logE("live pay, stripe succ, error = " + e.getMessage());
}
}
五、Google Pay配置
配置地址 https://pay.google.com/business/console/u/2/payment/BCR2DN6TZP4O3TIO
完成Google Pay配置
六、完整代码
/**
* Description: 国际版Google Pay
* ---------------------
* Author: xiangpan
* Date: 2020/10/14 6:01 PM
*/
public class GooglePayGlobe implements DefaultLifecycleObserver {
public static final int LOAD_PAYMENT_DATA_REQUEST_CODE = 5300;
public static final String LIVE_API = "pk_test_51Hc6UgKbT4q9TnA8xWZpZwKjXagTRmgyMC5q8HaQqgP1XmiPYRAsLCUMriIoLe5nR2gVtpRY39SeL8x7r00J3duNXzg";
//测试 ENVIRONMENT_TEST 正式 ENVIRONMENT_PRODUCTION
public static final int PAYMENTS_ENVIRONMENT = WalletConstants.ENVIRONMENT_TEST;
public static final List<String> SUPPORTED_NETWORKS = Arrays.asList(
"AMEX",
"DISCOVER",
"JCB",
"MASTERCARD",
"VISA");
public static final List<String> SUPPORTED_METHODS = Arrays.asList(
"PAN_ONLY",
"CRYPTOGRAM_3DS");
public static final String COUNTRY_CODE = "US";
public static final String CURRENCY_CODE = "USD";
public MutableLiveData<StripeDto> statusData = new MutableLiveData<>();
private PaymentActivity mActivity;
private PaymentsClient paymentsClient;
private Stripe mStripe;
private String mOrderId;
private String mSecret;
private int mCreateOrderRetryTime = 0;
private int mVerifyOrderRetryTime = 0;
private ChargeUnitDto mChargeUnitDto;
private String mCouponUuid;
private double mPrice;
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
mActivity = (com.ev.live.payment.PaymentActivity) owner;
paymentsClient = createPaymentsClient(mActivity);
statusData.observe(mActivity, stripeDto -> {
if (stripeDto != null && stripeDto.code == 0) {
Logger.logE("live pay stripe, observ " + stripeDto.isFail + " status = " + stripeDto.status);
if (stripeDto.status == StripeDto.STRIPE_ORDER_SUCC) {
mStripe = new Stripe(mActivity, LIVE_API);
mOrderId = stripeDto.orderId;
mSecret = stripeDto.client_secret;
mPrice = stripeDto.amount;
payWithGoogle(mPrice);
// AnalyticUtil.firebaseEvent(AnalyticUtil.STRIPE_ORDER_SUCC, getStaticsBundle());
} else if (stripeDto.status == StripeDto.STRIPE_ORDER_FAIL) {
if (mCreateOrderRetryTime < 3) {
PaymentNetManager.requestStripeOrder(true,mChargeUnitDto.id, mCouponUuid, statusData);
}else {
stripeDto.status = StripeDto.STRIPE_ORDER_FAIL_FINAL;
stripeDto.isFail = true;
statusData.postValue(stripeDto);
}
mCreateOrderRetryTime ++;
} else if (stripeDto.status == StripeDto.STRIPE_VERIFY_FAIL) {
if (mVerifyOrderRetryTime < 3) {
PaymentNetManager.verifyStripeOrder(true,mOrderId, statusData);
}
mVerifyOrderRetryTime++;
// AnalyticUtil.firebaseEvent(AnalyticUtil.STRIPE_VERIFY_FAIL, getStaticsBundle());
}
}
});
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
mActivity = null;
}
/**
* Google 支付
* @param dto
* @param couponUuid
*/
public void startPayment(ChargeUnitDto dto, String couponUuid) {
if (dto != null) {
mChargeUnitDto = dto;
mCouponUuid = couponUuid;
PaymentNetManager.requestStripeOrder(true,mChargeUnitDto.id, mCouponUuid, statusData);
}
// AnalyticUtil.threeChannelEvent(PAYPAL_ORDER_CLICK, getStaticsBundle());
}
private PaymentsClient createPaymentsClient(Activity activity) {
Wallet.WalletOptions walletOptions =
new Wallet.WalletOptions.Builder().setEnvironment(PAYMENTS_ENVIRONMENT).build();
return Wallet.getPaymentsClient(activity, walletOptions);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void payWithGoogle( double price) {
if (paymentsClient == null) {
paymentsClient = createPaymentsClient(mActivity);
}
Optional<JSONObject> paymentDataRequestJson = createPaymentDataRequest(price);
if (!paymentDataRequestJson.isPresent()) {
return;
}
PaymentDataRequest request =
PaymentDataRequest.fromJson(paymentDataRequestJson.get().toString());
if (request != null) {
if (mActivity!=null){
mActivity.showCircle2Loading();
}
AutoResolveHelper.resolveTask(
paymentsClient.loadPaymentData(request),
mActivity,
LOAD_PAYMENT_DATA_REQUEST_CODE
);
}
}
/**
* {
* "apiVersionMinor": 0,
* "apiVersion": 2,
* "paymentMethodData": {
* "description": "中国招商银行 (CMB) •••• 5019",
* "tokenizationData": {
* "type": "PAYMENT_GATEWAY",
* "token": "{\n \"id\": \"tok_1HcQIMBcf7rsT369XhdHf1\",\n \"object\": \"token\",\n \"card\": {\n \"id\": \"card_1HcQIMBcf7rsT369lDDy6PIM\",\n \"object\": \"card\",\n \"address_city\": null,\n \"address_country\": null,\n \"address_line1\": null,\n \"address_line1_check\": null,\n \"address_line2\": null,\n \"address_state\": null,\n \"address_zip\": null,\n \"address_zip_check\": null,\n \"brand\": \"Visa\",\n \"country\": \"US\",\n \"cvc_check\": null,\n \"dynamic_last4\": \"4242\",\n \"exp_month\": 10,\n \"exp_year\": 2025,\n \"funding\": \"credit\",\n \"last4\": \"5019\",\n \"metadata\": {\n },\n \"name\": \"name\",\n \"tokenization_method\": \"android_pay\"\n },\n \"client_ip\": \"173.194.101.160\",\n \"created\": 1602744638,\n \"livemode\": false,\n \"type\": \"card\",\n \"used\": false\n}\n"
* },
* "type": "CARD",
* "info": {
* "cardNetwork": "VISA",
* "cardDetails": "5019"
* }
* }
* }
*
* @param data
* @param resultCode
*/
public void onCheckResult(int resultCode,Intent data) {
switch (resultCode) {
case Activity.RESULT_OK: {
onGooglePayResult(data);
break;
}
case Activity.RESULT_CANCELED: {
errorShowAndRetry();
break;
}
case AutoResolveHelper.RESULT_ERROR: {
final Status status =
AutoResolveHelper.getStatusFromIntent(data);
errorShowAndRetry();
break;
}
default: {
// Do nothing.
}
}
}
private void onGooglePayResult(@NonNull Intent data) {
PaymentData paymentData = PaymentData.getFromIntent(data);
if (paymentData == null) {
errorShowAndRetry();
return;
}
try {
PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.createFromGooglePay(new JSONObject(paymentData.toJson()));
mStripe.createPaymentMethod(
paymentMethodCreateParams,
new ApiResultCallback<PaymentMethod>() {
@Override
public void onSuccess(@NonNull PaymentMethod result) {
paymentGotoStripe(result.id);
}
@Override
public void onError(@NonNull Exception e) {
errorShowAndRetry();
}
}
);
} catch (Exception e) {
errorShowAndRetry();
}
}
/**
* 得到PaymentMethodId 去stripe 支付
*/
private void paymentGotoStripe(String id){
if ( mStripe != null && !TextUtils.isEmpty(mSecret)) {
ConfirmPaymentIntentParams confirmParams = ConfirmPaymentIntentParams.createWithPaymentMethodId(id, mSecret);
mStripe.confirmPayment(mActivity, confirmParams);
Logger.logE("live pay, click pay ---");
} else if (mStripe == null) {
ToastUtils.showSystemToast(R.string.payment_in_progress_tips);
Logger.logE("live pay, click pay --- order is null");
}
}
/**
* Google --> stripe--> CallBack
* @param requestCode
* @param data
*/
public void paymentResultCallback(int requestCode,Intent data){
mStripe.onPaymentResult(requestCode, data, new PaymentResultCallback(mActivity));
}
/**
* 判断用户是否支持Google pay支付
*
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public void possiblyShowGooglePayButton(View view) {
if (paymentsClient == null) {
paymentsClient = createPaymentsClient(mActivity);
}
final Optional<JSONObject> isReadyToPayJson = getIsReadyToPayRequest();
if (!isReadyToPayJson.isPresent()) {
return;
}
IsReadyToPayRequest request = IsReadyToPayRequest.fromJson(isReadyToPayJson.get().toString());
Task<Boolean> task = paymentsClient.isReadyToPay(request);
task.addOnCompleteListener(mActivity,
new OnCompleteListener<Boolean>() {
@Override
public void onComplete(@NonNull Task<Boolean> task) {
if (task.isSuccessful()) {
if (task.getResult()) {
view.setVisibility(View.VISIBLE);
} else {
view.setVisibility(View.GONE);
}
} else {
view.setVisibility(View.GONE);
Log.w("isReadyToPay failed", task.getException());
}
}
});
}
@RequiresApi(api = Build.VERSION_CODES.N)
private Optional<JSONObject> createPaymentDataRequest(double priceCents) {
try {
JSONObject paymentDataRequest = getBaseRequest();
// 指定是否支持 Google Pay API 所支持的一种或多种付款方式。
paymentDataRequest.put("allowedPaymentMethods", new JSONArray().put(getCardPaymentMethod()));
// 有关根据用户是否同意交易来为交易授权的详细信息。包含总价和价格状态
paymentDataRequest.put("transactionInfo", getTransactionInfo(priceCents));
//商家信息
paymentDataRequest.put("merchantInfo", getMerchantInfo());
paymentDataRequest.put("shippingAddressRequired", false);
paymentDataRequest.put("emailRequired", false);
return Optional.of(paymentDataRequest);
} catch (JSONException e) {
return Optional.empty();
}
}
private JSONObject getCardPaymentMethod() throws JSONException {
JSONObject cardPaymentMethod = getBaseCardPaymentMethod();
JSONObject tokenizationSpec = new GooglePayConfig(LIVE_API).getTokenizationSpecification();
cardPaymentMethod.put("tokenizationSpecification", tokenizationSpec);
return cardPaymentMethod;
}
/**
* 金钱信息
*/
private JSONObject getTransactionInfo(double price) throws JSONException {
JSONObject transactionInfo = new JSONObject();
transactionInfo.put("totalPrice", price+"");
transactionInfo.put("totalPriceStatus", "FINAL");
transactionInfo.put("countryCode", COUNTRY_CODE);
transactionInfo.put("currencyCode", CURRENCY_CODE);
transactionInfo.put("checkoutOption", "COMPLETE_IMMEDIATE_PURCHASE");
return transactionInfo;
}
/**
* 商家信息
* merchantId 商家Id
*/
private JSONObject getMerchantInfo() throws JSONException {
return new JSONObject().put("merchantName", "Guruji").put("merchantId", "BCR2DN6T4XFIT7KA");
}
@RequiresApi(api = Build.VERSION_CODES.N)
private Optional<JSONObject> getIsReadyToPayRequest() {
try {
JSONObject isReadyToPayRequest = getBaseRequest();
isReadyToPayRequest.put(
"allowedPaymentMethods", new JSONArray().put(getBaseCardPaymentMethod()));
isReadyToPayRequest.put("existingPaymentMethodRequired", true);
return Optional.of(isReadyToPayRequest);
} catch (JSONException e) {
return Optional.empty();
}
}
private JSONObject getBaseCardPaymentMethod() throws JSONException {
JSONObject cardPaymentMethod = new JSONObject();
cardPaymentMethod.put("type", "CARD");
JSONObject parameters = new JSONObject();
parameters.put("allowedAuthMethods", getAllowedCardAuthMethods());
parameters.put("allowedCardNetworks", getAllowedCardNetworks());
// Optionally, you can add billing address/phone number associated with a CARD payment method.
parameters.put("billingAddressRequired", false);
cardPaymentMethod.put("parameters", parameters);
return cardPaymentMethod;
}
private JSONArray getAllowedCardAuthMethods() {
return new JSONArray(SUPPORTED_METHODS);
}
private JSONArray getAllowedCardNetworks() {
return new JSONArray(SUPPORTED_NETWORKS);
}
private JSONObject getBaseRequest() throws JSONException {
return new JSONObject()
.put("apiVersion", 2)
.put("apiVersionMinor", 0);
}
private Bundle getStaticsBundle() {
Bundle bundle = new Bundle();
if (!TextUtils.isEmpty(mOrderId)) {
bundle.putString("order_id", mOrderId);
}
return bundle;
}
private void errorShowAndRetry () {
StripeDto stripeDto = new StripeDto();
stripeDto.isFail = true;
stripeDto.status = StripeDto.STRIPE_PAY_FAIL;
statusData.setValue(stripeDto);
}
private final class PaymentResultCallback implements ApiResultCallback<PaymentIntentResult> {
@NonNull
private final WeakReference<PaymentActivity> activityRef;
PaymentResultCallback(@NonNull PaymentActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public void onSuccess(@NonNull PaymentIntentResult result) {
PaymentActivity activity = activityRef.get();
if (activity == null) {
return;
}
PaymentIntent paymentIntent = result.getIntent();
PaymentIntent.Status status = paymentIntent.getStatus();
if (status == PaymentIntent.Status.Succeeded) {
PaymentNetManager.verifyStripeOrder(true,mOrderId, statusData);
} else if (status == PaymentIntent.Status.RequiresPaymentMethod) {
errorShowAndRetry();
}
Logger.logE("live pay, stripe succ, status = " + status);
}
@Override
public void onError(@NonNull Exception e) {
PaymentActivity activity = activityRef.get();
if (activity == null) {
return;
}
errorShowAndRetry();
Logger.logE("live pay, stripe succ, error = " + e.getMessage());
}
}
}
希望可以帮助遇到同样问题的你😁😁😁😁
如有建议和意见,请及时沟通。