Checkout V3 with Cash App Pay
NEW (v4.3.0)
Table of contents
- Initial Setup
- Authorization steps
- Step 1: Set merchant configurations
- Step 2: Create
CashAppPay
instance and register a state listener - Step 3: Begin checkout
- Step 4: Save result
- Step 5: Create customer request
- Step 6: Respond to ReadyToAuthorize
- Step 7: Authorize when customer clicks button
- Step 8: Disable button when authorization is in flight
- Step 9: Respond to Approved or Declined
- Step 10: Pass grant back to Afterpay
Cash App Pay is currently available in the following region(s): US
This method requires importing and implementing the Cash App PayKit SDK in addition to the Afterpay SDK.
Follow along with the example implementation.
Initial Setup
Import the Cash App Pay Kit Dependency
You can get the latest version of the SDK from Maven. This is the import definition using Gradle:
implementation "app.cash.paykit:core:2.3.0"
For definitions of other build systems, see Cash App Pay Kit on Maven Central.
Version
v2.3.0
of the SDK is12.3 kB
.
Implement Deep Linking
The authorization flow will bring Cash App to the foreground on the Customer’s device. After the Customer either authorizes or declines, the Cash App Pay SDK will attempt to return your app to the foreground. This is accomplished by declaring an intent filter on your app’s Android Manifest. You will pass a corresponding redirect URI when launching Cash App Pay SDK, see steps below.
<!-- Intent filter to allow Cash App Pay SDK to redirect to your app.
Consider creating a custom scheme to ensure only your app is launched. -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data
android:host="example.com"
android:scheme="example" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Authorization steps
Step 1: Set merchant configurations
Confirm that the Afterpay SDK is configured per the [instructions][configure-afterpay] before attempting to access
Afterpay.environment.payKitClientId
.
Afterpay.setConfigurationV3(configurations)
Step 2: Create CashAppPay
instance and register a state listener
To create a new instance of CashAppPay
, you must pass the clientId
. This is a required field. This can be retrieved through the Afterpay object: Afterpay.environment.payKitClientId
.
You should use CashAppPayFactory
to create an instance of CashAppPay
. When doing so, you must specify the environment you will use, Sandbox or Production. The function createSandbox()
will create an instance in the Sandbox environment.
You should use the Sandbox environment during the development phase and the Production environment for your production releases.
Optional: We recommend using the Cash App Sandbox App to test the payment flow in the Sandbox environment during your development phase.
private lateinit var cashAppPay: CashAppPay
private val cashAppPayListener =
object : CashAppPayListener {
override fun cashAppPayStateDidChange(newState: CashAppPayState) {
// TODO()
}
}
...
private fun initializeCashAppSDK() {
cashAppPay = CashAppPayFactory.createSandbox(Afterpay.environment.payKitClientId) // sandbox
or
cashAppPay = CashAppPayFactory.create(Afterpay.environment.payKitClientId) // production
cashAppPay.registerForStateUpdates(cashAppPayListener)
}
States
CashAppPayState
is a sealed class. Some states are for information only, but most will drive the logic of your integration. The most critical states to handle are in the table below:
State | Description |
---|---|
ReadyToAuthorize | You should show the Cash App Pay button in your UI and call authorizeCustomerRequest() when it is tapped. |
Approved | Grants are ready for your app to send to Afterpay SDK. |
Declined | Customer has declined the Cash App Pay authorization and must start the flow over or choose a new payment method. |
CashAppPayExceptionState | The general wrapper state for exceptions. These can range from integration errors to network errors. The exception states are emitted only for unrecoverable error states. |
Handling of each of these states is outlined in further detail below.
Step 3: Begin checkout
Begin checkout process by requesting data from Afterpay. Optionally, supply data on the customer, order total, and items being purchased. Set configuration
now if you failed to set it previously.
Afterpay.beginCheckoutV3WithCashAppPay(
consumer = // TODO,
orderTotal = // TODO,
items = // TODO,
configuration = // TODO, optional,
).let { result: Result<CheckoutV3CashAppPay> ->
// see next step...
}
Step 4: Save result
You will receive a Result<CheckoutV3CashAppPay>
on success or failure. When successful, save the result
for later and begin the customer request process.
private var checkoutV3CashAppPay: CheckoutV3CashAppPay? = null
result.onSuccess {
checkoutV3CashAppPay = it
createCashAppPayCustomerRequest(it)
}
result.onFailure { error ->
// TODO handle error
}
Step 5: Create customer request
Using the CheckoutV3CashAppPay
from the previous step, create a OneTimeAction
with Cash App Pay SDK. Also supply the redirect URI which matches the IntentFilter
defined in your AndroidManifest.xml
(see above)
fun createCashAppPayCustomerRequest(checkoutV3CashAppPay: CheckoutV3CashAppPay) {
val redirectUri = "example://example.com/"
// must convert from dollars to cents
val cents = (checkoutV3CashAppPay.amount * 100).toInt()
val action = CashAppPayPaymentAction.OneTimeAction(
currency = USD,
amount = cents,
scopeId = checkoutV3CashAppPay.brandId,
)
cashAppPay.createCustomerRequest(
action,
redirectUri = redirectUri,
)
}
Step 6: Respond to ReadyToAuthorize
Inside the CashAppPayListener
you created earlier, respond to ReadyToAuthorize
state by enabling your “Pay With Cash App” button:
private val cashAppPayListener =
object : CashAppPayListener {
override fun cashAppPayStateDidChange(newState: CashAppPayState) {
when (newState) {
is ReadyToAuthorize -> {
lifecycleScope.launch { // jump back to UI thread to update UI
bindings.cashappPayButton.isEnabled = true
}
}
... // other states
}
}
}
Step 7: Authorize when customer clicks button
When beginning the authorization request, Cash App Pay SDK will redirect the Customer to Cash App, where they can approve or decline the grant request.
bindings.cashappPayButton.apply {
setOnClickListener { _ ->
cashAppPay.authorizeCustomerRequest()
}
}
Step 8: Disable button when authorization is in flight
Avoid duplicate button clicks by disabling once an authorization is detected.
private val cashAppPayListener =
object : CashAppPayListener {
override fun cashAppPayStateDidChange(newState: CashAppPayState) {
when (newState) {
Authorizing -> {
bindings.cashappPayButton.isEnabled = false
}
... // other states
}
}
}
Step 9: Respond to Approved or Declined
Cash App Pay SDK will redirect back to your app (per the supplied redirect URI). CashAppPayListener
will receive either a Approved
or Declined
response.
private val cashAppPayListener =
object : CashAppPayListener {
override fun cashAppPayStateDidChange(newState: CashAppPayState) {
Log.d(tag, "cashAppPayStateDidChange: ${newState::class.java}")
when (newState) {
is Approved -> {
newState.responseData.apply {
grants?.get(0)?.let { grant: Grant ->
confirmCheckoutWithAfterpay(
grantId = grant.id,
customerId = grant.customerId,
)
}
}
}
Declined -> {
// TODO
}
... // other states
}
}
}
Step 10: Pass grant back to Afterpay
By combining the CheckoutV3CashAppPay
you stored before, with the newly received grantId
and customerId
, you can now confirm checkout with Afterpay
fun confirmCheckoutWithAfterpay(
grantId: String,
customerId: String,
) {
checkoutV3CashAppPay?.let { it ->
Afterpay.confirmCheckoutV3WithCashAppPay(
grantId = grantId,
customerId = customerId,
token = it.token,
singleUseCardToken = it.singleUseCardToken,
jwt = it.jwt,
configuration = createCheckoutV3Configuration(),
).let { result: Result<CheckoutV3Data> ->
result.onSuccess {
// TODO next step
}
result.onFailure {
// TODO
}
}
}
}
Step 11: Receive one-time use card details and process
The success result will contain card details, tokens, and a valid-until time. Pass these back to your own server and process them through your normal card processing infrastructure, or pass them on to another mobile card processing SDK .
result.onSuccess {
it.cardDetails
it.tokens
it.cardValidUntil
}
Step 12: Unregister
You should also use the Unregister function when you’re done with the SDK:
cashAppPay.unregisterFromStateUpdates()