Skip to content

Instantly share code, notes, and snippets.

@krunk4ever
Last active May 21, 2020 17:28
Show Gist options
  • Save krunk4ever/79cdc15d2e09b15a5826d9d98c2c3dda to your computer and use it in GitHub Desktop.
Save krunk4ever/79cdc15d2e09b15a5826d9d98c2c3dda to your computer and use it in GitHub Desktop.
PaymentDataRequest support for Google Pay API v2 on Android
import android.os.Bundle
import com.google.android.gms.wallet.PaymentData
import com.google.android.gms.wallet.PaymentDataRequest
import com.google.android.gms.wallet.PaymentMethodTokenizationParameters
import com.google.android.gms.wallet.WalletConstants
import org.json.JSONArray
import org.json.JSONObject
/**
* Google Pay API request configurations.
*
* @see [Google Pay API Android
* documentation](https://developers.google.com/pay/api/android/)
*/
object GooglePay {
/**
* Create a Google Pay API base request object with properties used in all requests.
*
* @return Google Pay API base request object
*/
private val baseRequest: JSONObject
get() = JSONObject()
.put("apiVersion", 2)
.put("apiVersionMinor", 0)
/**
* Identify your gateway and your app's gateway merchant identifier.
*
* The Google Pay API response will return an encrypted payment method capable of being charged
* by a supported gateway after payer authorization.
*
* @return payment data tokenization for the CARD payment method
* @see [PaymentMethodTokenizationSpecification](https://developers.google.com/pay/api/android/reference/object.PaymentMethodTokenizationSpecification)
*/
private fun tokenizationSpecification(tokenizationParameters: PaymentMethodTokenizationParameters): JSONObject {
val tokenizationSpecification = JSONObject()
.put("type", "PAYMENT_GATEWAY")
// inject all the token parameters into json object
tokenizationParameters.parameters?.let { bundle ->
tokenizationSpecification.put("parameters", bundle.toJSONObject())
}
return tokenizationSpecification
}
/**
* Card networks supported by your app and your gateway.
*
* @return allowed card networks
* @see [CardParameters](https://developers.google.com/pay/api/android/reference/object.CardParameters)
*/
private fun allowedCardNetworks(allowedCardNetworkIDs: Collection<Int>): JSONArray {
return JSONArray(allowedCardNetworkIDs.mapNotNull { cardNetworkName(it) })
}
private fun cardNetworkName(cardNetworkID: Int): String? {
return when (cardNetworkID) {
WalletConstants.CARD_NETWORK_AMEX -> "AMEX"
WalletConstants.CARD_NETWORK_DISCOVER -> "DISCOVER"
WalletConstants.CARD_NETWORK_JCB -> "JCB"
WalletConstants.CARD_NETWORK_MASTERCARD -> "MASTERCARD"
WalletConstants.CARD_NETWORK_VISA -> "VISA"
WalletConstants.CARD_NETWORK_INTERAC -> "INTERAC"
WalletConstants.CARD_NETWORK_OTHER -> "OTHER"
else -> null
}
}
/**
* Card authentication methods supported by your app and your gateway.
*
* @return allowed card authentication methods
* @see [CardParameters](https://developers.google.com/pay/api/android/reference/object.CardParameters)
*/
private fun allowedCardAuthMethods(allowedCardAuthMethodIDs: Collection<Int>): JSONArray {
return JSONArray(allowedCardAuthMethodIDs.mapNotNull { allowedCardAuthMethodName(it) })
}
private fun allowedCardAuthMethodName(allowedCardAuthMethodID: Int): String? {
return when (allowedCardAuthMethodID) {
WalletConstants.PAYMENT_METHOD_CARD -> "PAN_ONLY"
WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD -> "CRYPTOGRAM_3DS"
else -> null
}
}
/**
* Describe your app's support for the CARD payment method.
*
* The provided properties are applicable to both an IsReadyToPayRequest and a
* PaymentDataRequest.
*
* @return a CARD PaymentMethod object describing accepted cards
* @see [PaymentMethod](https://developers.google.com/pay/api/android/reference/object.PaymentMethod)
*/
private fun baseCardPaymentMethod(
allowedCardNetworkIDs: Collection<Int>,
allowedCardAuthMethodIDs: Collection<Int>,
billingAddressParameters: JSONObject?
): JSONObject {
val parameters = JSONObject()
.put("allowedAuthMethods", allowedCardAuthMethods(allowedCardAuthMethodIDs))
.put("allowedCardNetworks", allowedCardNetworks(allowedCardNetworkIDs))
billingAddressParameters?.let {
parameters.put("billingAddressRequired", true)
parameters.put("billingAddressParameters", it)
}
val cardPaymentMethod = JSONObject()
.put("type", "CARD")
.put("parameters", parameters)
return cardPaymentMethod
}
/**
* Describe the expected returned payment data for the CARD payment method.
*
* @return a CARD PaymentMethod describing accepted cards and optional fields
* @see [PaymentMethod](https://developers.google.com/pay/api/android/reference/object.PaymentMethod)
*/
fun cardPaymentMethod(
tokenizationParameters: PaymentMethodTokenizationParameters,
allowedCardNetworkIDs: Collection<Int>,
allowedCardAuthMethodIDs: Collection<Int>,
billingAddressParameters: JSONObject? = null
): JSONObject {
val cardPaymentMethod = baseCardPaymentMethod(allowedCardNetworkIDs, allowedCardAuthMethodIDs, billingAddressParameters)
.put("tokenizationSpecification", tokenizationSpecification(tokenizationParameters))
return cardPaymentMethod
}
/**
* Provide Google Pay API with a payment amount, currency, and amount status.
*
* @return information about the requested payment
* @see [TransactionInfo](https://developers.google.com/pay/api/android/reference/object.TransactionInfo)
*/
fun transactionInfo(price: String, currency: String, priceStatusID: Int): JSONObject {
val transactionInfo = JSONObject()
.put("totalPrice", price)
.put("currencyCode", currency)
priceStatusName(priceStatusID)?.let {
transactionInfo.put("totalPriceStatus", it)
}
return transactionInfo
}
private fun priceStatusName(priceStatusID: Int): String? {
return when (priceStatusID) {
WalletConstants.TOTAL_PRICE_STATUS_NOT_CURRENTLY_KNOWN -> "NOT_CURRENTLY_KNOWN"
WalletConstants.TOTAL_PRICE_STATUS_ESTIMATED -> "ESTIMATED"
WalletConstants.TOTAL_PRICE_STATUS_FINAL -> "FINAL"
else -> null
}
}
/**
* Information about the merchant requesting payment information.
*
* @return information about the merchant
* @see [MerchantInfo](https://developers.google.com/pay/api/android/reference/object.MerchantInfo)
*/
fun merchantInfo(merchantName: String): JSONObject {
return JSONObject()
.put("merchantName", merchantName)
}
fun billingAddressParameters(billingAddressFormatID: Int, phoneNumberRequired: Boolean): JSONObject {
val billingAddressParameters = JSONObject()
.put("phoneNumberRequired", phoneNumberRequired)
billingAddressFormatName(billingAddressFormatID)?.let {
billingAddressParameters.put("format", it)
}
return billingAddressParameters
}
private fun billingAddressFormatName(billingAddressFormatID: Int): String? {
return when (billingAddressFormatID) {
WalletConstants.BILLING_ADDRESS_FORMAT_MIN -> "MIN"
WalletConstants.BILLING_ADDRESS_FORMAT_FULL -> "FULL"
else -> null
}
}
/**
* An object describing information requested in a Google Pay payment sheet.
*
* @return payment data expected by your app
* @see [PaymentDataRequest](https://developers.google.com/pay/api/android/reference/object.PaymentDataRequest)
*/
fun paymentDataRequest(
cardPaymentMethod: JSONObject,
transactionInfo: JSONObject,
merchantInfo: JSONObject
): PaymentDataRequest {
val paymentDataRequest = baseRequest
.put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod))
.put("transactionInfo", transactionInfo)
.put("merchantInfo", merchantInfo)
return PaymentDataRequest.fromJson(paymentDataRequest.toString())
}
}
fun PaymentData.billingAddressCountryCode(): String? {
// when you create PaymentData from intent/json, cardInfo and billingAddress are no longer populated
return cardInfo?.billingAddress?.countryCode
?: JSONObject(toJson())
.optJSONObject("paymentMethodData")
.optJSONObject("info")
.optJSONObject("billingAddress")
.optString("countryCode")
}
fun PaymentData.billingAddressPostalCode(): String? {
// when you create PaymentData from intent/json, cardInfo and billingAddress are no longer populated
return cardInfo?.billingAddress?.postalCode
?: JSONObject(toJson())
.optJSONObject("paymentMethodData")
.optJSONObject("info")
.optJSONObject("billingAddress")
.optString("postalCode")
}
fun Bundle.toJSONObject(): JSONObject {
val jsonObject = JSONObject()
keySet().forEach { key ->
getString(key)?.let { value ->
jsonObject.put(key, value)
}
}
return jsonObject
}
import com.braintreepayments.api.BraintreeFragment;
import com.braintreepayments.api.GooglePayment;
import com.braintreepayments.api.interfaces.BraintreeResponseListener;
import com.google.android.gms.wallet.AutoResolveHelper;
import com.google.android.gms.wallet.PaymentData;
import com.google.android.gms.wallet.PaymentDataRequest;
import com.google.android.gms.wallet.PaymentsClient;
import com.google.android.gms.wallet.Wallet;
import com.google.android.gms.wallet.WalletConstants;
import org.json.JSONObject;
public class GooglePaymentClient implements GooglePaymentApi {
private final PaymentsClient googlePaymentsClient;
@Override
public void loadGooglePay(String formattedPrice, int requestCode) {
JSONObject billingAddressParameters = GooglePay.INSTANCE.billingAddressParameters(WalletConstants.BILLING_ADDRESS_FORMAT_MIN, false);
JSONObject cardPaymentMethod = GooglePay.INSTANCE.cardPaymentMethod(
parameters,
allowedCardNetworks,
Arrays.asList(WalletConstants.PAYMENT_METHOD_CARD, WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD),
billingAddressParameters);
JSONObject transactionInfo = GooglePay.INSTANCE.transactionInfo(
formattedPrice,
currencyFormatter.getLocalCurrencyString(),
WalletConstants.TOTAL_PRICE_STATUS_ESTIMATED);
JSONObject merchantInfo = GooglePay.INSTANCE.merchantInfo(
braintreeFragment.getContext().getString(R.string.merchant_name)
);
PaymentDataRequest paymentDataRequest = GooglePay.INSTANCE.paymentDataRequest(
cardPaymentMethod,
transactionInfo,
merchantInfo);
AutoResolveHelper.resolveTask(
googlePaymentsClient.loadPaymentData(paymentDataRequest),
activity,
requestCode);
}
@Override
public void tokenizeGooglePayment(PaymentData paymentData) {
GooglePayment.tokenize(braintreeFragment, paymentData);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment