Checkout V2 with Cash App Pay
NEW (v4.3.0)
Table of contents
- Step 1: Import the Cash App Pay Kit Dependency
- Step 2: Create a Cash App Pay Kit SDK Instance
- Step 3: Register for State Updates
- Step 4: Implement Deep Linking
- Step 5: Create a Customer Request
- Step 6: Authorize the Customer Request
- Step 7: Pass Grants to the Backend and Capture Payment
- Sequence Diagram
Cash App Pay is currently available in the following region(s): US
With our latest enhancements, you can now support taking Cash App Pay payments using your Afterpay merchant account. To do this, you must generate a token by sending a server-to-server call to the Afterpay API Create Checkout endpoint with the parameter isCashAppPay
set to true
. This method requires importing and implementing the Cash App Pay Kit SDK.
When creating a checkout token, you must set both
redirectConfirmUrl
andredirectCancelUrl
. If they are not set, an error will be returned from the server and the SDK will output a malformed JSON error. The SDK’s example merchant server sets the parameters here. ee more details at Redirect Method in the Standard Checkout API.
Step 1: 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
.
Step 2: Create a Cash App Pay Kit SDK Instance
To create a new instance of the Cash App Pay Kit SDK, you must pass the clientId
. This is a required field. This can be retrieved through the Afterpay object: Afterpay.environment.payKitClientId
.
Confirm that the Afterpay SDK is configured per the instructions before attempting to access
Afterpay.environment.payKitClientId
You should use CashAppPayFactory
to create an instance of the Cash App Pay Kit SDK. When doing so, you must specify the environment you will use, Sandbox or Production. The function createSandbox()
will create an SDK instance in the Sandbox environment.
You should use the Sandbox environment during the development phase and the Production environment for your production releases.
Creating a Sandbox Cash App Pay Kit SDK instance:
val cashAppPay : CashAppPay =
CashAppPayFactory.createSandbox(Afterpay.environment.payKitClientId)
Creating a Production Cash App Pay Kit SDK instance:
val cashAppPay : CashAppPay =
CashAppPayFactory.create(Afterpay.environment.payKitClientId)
Optional: We recommend using the Cash App Sandbox App to test the payment flow in the Sandbox environment during your development phase.
Step 3: Register for State Updates
To receive updates from Pay Kit, you’ll need to implement the CashAppPayListener
interface. The interface exposes a single function, which gets called whenever there’s an internal state change emitted by the SDK:
interface CashAppPayListener {
fun cashAppPayStateDidChange(newState: CashAppPayState)
}
You register with the SDK instance you’ve created above:
private val cashAppPayListener =
object : CashAppPayListener {
override fun cashAppPayStateDidChange(newState: CashAppPayState) {
// TODO
}
}
cashAppPay.registerForStateUpdates(cashAppPayListener)
You should also use the Unregister function when you’re done with the SDK:
cashAppPay.unregisterFromStateUpdates()
States
CashAppPayState
is a sealed class. Some states are for information only, but most will drive the logic of your integration. See Cash App Pay SDK documentation for a complete list of states. 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 backend to use and to create a payment. |
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. |
Step 4: 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 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 then pass a corresponding redirect URI when launching Cash APP SDK, see next step.
<!-- 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>
You can also use a Verified App Link and https
scheme. However that is outside the scope of example.
Step 5: Create a Customer Request
You can create a customer request as soon as you know the amount you’d like to charge or if you’d like to create an on-file payment request. You can create this request as soon as your checkout screen loads, so that your customer can authorize the request without delay.
Step 5A: Sign the Order Token
After retrieving the token from your server-to-server call, you must sign the order, so that you can retrieve the JWT and associated data. This can be done either by the suspending function or with the asynchronous version of it.
Example of the suspending function:
Afterpay.signCashAppOrder(token) { cashAppData ->
when (cashAppData) {
is CashAppSignOrderResult.Success -> TODO("Create the Pay Kit customer request")
is CashAppSignOrderResult.Failure -> TODO("Display an error and restart payment flow")
}
}
Example of the asynchronous version of the suspending function:
Afterpay.signCashAppOrderAsync(token) { cashAppData ->
when (cashAppData) {
is CashAppSignOrderResult.Success -> TODO("Create the Pay Kit customer request")
is CashAppSignOrderResult.Failure -> TODO("Display an error and restart payment flow")
}
}
Step 5B: Create a Pay Kit Customer Request
To charge a one-time payment, your Create Request call might look like this (in the following example, cashAppData
is the response object that is returned in the trailing lambda in step 5A, which would echo the amount from your server-to-server Create Checkout call):
val request = CashAppPayPaymentAction.OneTimeAction(
currency = CashAppPayCurrency.USD,
amount = TODO(), // the amount of the transaction
scopeId = cashAppData.merchantId,
)
cashAppPay.createCustomerRequest(request, cashAppData.redirectUri)
Step 6: Authorize the Customer Request
Step 6A: Add an Authorize Request Event to Cash App Pay button
Once the Cash App Pay Kit SDK is in the ReadyToAuthorize
state, you can display the Cash App Pay button. When the customer taps the button, you can authorize the customer request. See Cash Button Docs to learn more about the Cash App Pay button component.
cashAppPay.authorizeCustomerRequest()
Currently, the Button provided by the SDK is unmanaged. This means that it’s a stylized button which isn’t aware of SDK events out-of-the-box. It is the developer’s responsibility to call the above method when the button is tapped and also manage any disabled and loading states.
Your app will redirect to Cash App for authorization. When the authorization is completed, your redirect URI will be called to open your app. The SDK will fetch your authorized request and return it to your callback listener as one of 2 states: Approved
or Declined
.
Step 6B: Validate the Cash App Pay Order
This step must not be skipped
Finally, you must validate the Cash App order. This will look like the following example:
Afterpay.validateCashAppOrder(
jwt,
customerResponseData.customerProfile!!.id,
grant.id,
) { validationResult ->
when (validationResult) {
is CashAppValidationResponse.Success -> TODO("Capture payment with token and grant id")
is CashAppValidationResponse.Failure -> TODO("Handle an invalid Cash App order")
}
}
Step 7: Pass Grants to the Backend and Capture Payment
The Approved
state will contain a Grants list object associated with it and it can be used with Afterpay’s Immediate Payment Capture or Deferred Payment Auth API. Pass the grant ID along with the token to capture/authorize using a server-to-server request.
Sequence Diagram
The below diagram describes the happy path.
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#00c846',
'primaryTextColor': '#FFFFFF',
'primaryBorderColor': '#dfdfdf',
'signalTextColor': '#000000',
'signalColor': '#000000',
'secondaryColor': '#006100',
'tertiaryColor': '#fff'
}
}
}%%
sequenceDiagram
participant App
participant Afterpay SDK
participant Cash App Pay SDK
participant Proxy Server
participant Afterpay API
Note over App,Afterpay API: Setup
App->>Afterpay SDK: Configure the SDK
Note over App,Afterpay SDK: Required for setting environment
App->>Cash App Pay SDK: Create Cash App Pay instance
Note over App,Cash App Pay SDK: Ensure same environment<br>as Afterpay SDK config
App->>App: Implement<br>deep linking
App->>Cash App Pay SDK: Register for state updates
Note over App,Afterpay API: Create Customer Request and Capture
App->>Proxy Server: Get Token Request
Proxy Server->>Afterpay API: Create Checkout Request
Note over Proxy Server,Afterpay API: Ensure same environment<br>as Afterpay SDK config<br><br>Request body should<br>contain `isCashAppPay: true`
Afterpay API-->>Proxy Server: Create Checkout Response
Note over Afterpay API,Proxy Server: Body contains a token
Proxy Server-->>App: Get Token Response
App->>Afterpay SDK: Sign the token
Afterpay SDK->>Afterpay API: Token signing request
Afterpay API-->>Afterpay SDK: Token signing response
Afterpay SDK-->>App: Decoded token data
App->>Cash App Pay SDK: Create a customer request
App->>Cash App Pay SDK: Authorize the customer request
App->>Afterpay SDK: Validate the order
App->>Proxy Server: Upon approved state send capture request
Note over App,Proxy Server: Pass the Grant Id (from the approved state)<br>and token in the body
Proxy Server->>Afterpay API: Capture Payment Request
Afterpay API-->>Proxy Server: Capture Payment Response
Proxy Server-->>App: Capture Payment Response
App->>App: Handle payment<br>capture response