Checkout V2 with Cash App Pay

NEW (v4.3.0)

Table of contents

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 and redirectCancelUrl. 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 is 12.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. 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 customemr 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 customemr 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 Respnse

  App->>App: Handle payment<br>capture response