Google Pay 谷歌支付(gateway = stripe)
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
} 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());
}
}
}
```
希望可以幫助遇到同樣問題的你😁😁😁😁 如有建議和意見,請及時溝通。