Skip to navigation

Cash App Pay on iOS

Accept Cash App Pay in your iOS app with your Afterpay merchant account. The Afterpay SDK works with the Cash App Pay Kit iOS SDK: Pay Kit sends the customer to Cash App to approve the payment, and the Afterpay SDK connects that approval to your Afterpay order.

Cash App Pay is available in the US.

You can accept Cash App Pay in two ways:

FlowHow it worksYour server
Server checkoutYour server creates an Afterpay checkout for Cash App Pay. After the customer approves the payment in Cash App, your server captures it.Creates the checkout and captures the payment.
Afterpay ButtonThe SDK creates the checkout from the order details. After the customer approves the payment, the SDK returns a one-time virtual card.Processes the virtual card like any other card.

Set up Pay Kit

Both flows use the same Pay Kit setup.

Install Pay Kit

Add the cash-app-pay-ios-sdk package with Swift Package Manager:

.package(
url: "https://github.com/cashapp/cash-app-pay-ios-sdk",
.upToNextMajor(from: "0.6.1")
)

Add the PayKit product to your app target, and add PayKitUI if you use the Cash App Pay button. You can also install Pay Kit with CocoaPods: pod 'CashAppPayKit' and pod 'CashAppPayKitUI'.

Handle the redirect from Cash App

After the customer approves or declines the payment, Cash App opens your app with a redirect URL. Register a custom URL scheme or a universal link for your app. Then post CashAppPay.RedirectNotification when your app opens the URL:

import PayKit
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
guard let url = URLContexts.first?.url else { return }
NotificationCenter.default.post(
name: CashAppPay.RedirectNotification,
object: nil,
userInfo: [UIApplication.LaunchOptionsKey.url: url]
)
}
}

Observe Pay Kit states

Pay Kit reports its progress through the CashAppPayObserver protocol. Update your UI for each state:

extension CheckoutViewController: CashAppPayObserver {
func stateDidChange(to state: CashAppPayState) {
switch state {
case .readyToAuthorize(let customerRequest):
// Enable the Cash App Pay button.
self.customerRequest = customerRequest
case .approved(let customerRequest, let grants):
// Complete the order with the first grant. See the next sections.
break
case .declined:
// Let the customer choose another payment method.
break
case .redirecting, .polling:
// Optionally show a loading indicator.
break
case .integrationError, .apiError, .unexpectedError, .networkError:
// Hide Cash App Pay or let the customer try again.
break
default:
break
}
}
}
StateWhat to do
readyToAuthorizeShow the Cash App Pay button. When the customer taps it, call authorizeCustomerRequest(_:).
approvedThe customer approved the payment. Use the grant to complete the order.
declinedThe customer declined the payment. Start again or offer another payment method.
redirecting, pollingPay Kit is opening Cash App or waiting for the result. Optionally show a loading indicator.
integrationErrorYour integration moved between states in an unsupported order. Fix the integration.
apiErrorThe Cash App Pay API isn’t available. Hide Cash App Pay for now.
networkErrorThe device couldn’t connect. Let the customer try again.
unexpectedErrorContact Cash App developer support.

If Cash App isn’t installed, the customer is sent to a web page to download it, and Pay Kit stays in the polling state. Set a timeout for this state. When it expires, dismiss any loading indicator and let the customer start again or choose another payment method.

Server checkout

This flow uses the merchant configuration that you set with Afterpay.setConfiguration(_:). For details, see Configure the SDK.

Step 1: Create a checkout for Cash App Pay

Your server calls Create Checkout with isCashAppPay set to true and returns the token to your app.

Set both merchant.redirectConfirmUrl and merchant.redirectCancelUrl in the request. If you don’t, Create Checkout returns an error.

Step 2: Create Pay Kit

Create Pay Kit with Afterpay.cashAppClientId and the same environment as your Afterpay configuration:

private var payKit: CashAppPay?
func setUpPayKit() {
// cashAppClientId is nil until you set the Afterpay configuration.
guard let clientID = Afterpay.cashAppClientId else { return }
let payKit = CashAppPay(
clientID: clientID,
endpoint: Afterpay.environment == .production
? .production
: .sandbox
)
payKit.addObserver(self)
self.payKit = payKit
}

Step 3: Sign the token and create a customer request

Sign the checkout token with the Afterpay SDK. Then use the signing data to create a Pay Kit customer request. Do this as soon as your checkout screen loads, so that the customer can approve the payment without waiting.

The completion handler runs on a background queue, so move to the main queue before you update your UI. If you haven’t set the configuration, or Afterpay.enabled is false, the SDK doesn’t call the completion handler.

Afterpay.signCashAppOrderToken(token) { result in
DispatchQueue.main.async {
switch result {
case .success(let cashData):
self.cashData = cashData
self.payKit?.createCustomerRequest(
params: CreateCustomerRequestParams(
actions: [
.oneTimePayment(
scopeID: cashData.brandId,
money: Money(amount: cashData.amount, currency: .USD)
),
],
// Your app's redirect URL from "Handle the redirect".
redirectURL: URL(string: "yourapp://cashapp")!,
referenceID: nil,
metadata: nil
)
)
case .failed:
// Hide the Cash App Pay button, or create a new checkout.
self.hideCashAppPay()
}
}
}

Pay Kit moves to creatingCustomerRequest, then to readyToAuthorize.

Step 4: Authorize the customer request

When the customer taps the Cash App Pay button, authorize the request. Pay Kit opens Cash App.

@objc func didTapCashAppPay() {
guard let customerRequest else { return }
payKit?.authorizeCustomerRequest(customerRequest)
}

Step 5: Validate the order

When Pay Kit moves to approved, validate the order with the Afterpay SDK. Don’t skip this step. As with signing, the completion handler runs on a background queue.

case .approved(let customerRequest, let grants):
guard
let customerId = customerRequest.customerProfile?.id,
let grant = grants.first
else { return }
Afterpay.validateCashAppOrder(
jwt: cashData.jwt,
customerId: customerId,
grantId: grant.id
) { result in
DispatchQueue.main.async {
switch result {
case .success:
// Ask your server to capture the payment.
self.completeOrder()
case .failed:
// Start the payment again.
self.restartPayment()
}
}
}

Step 6: Capture the payment

Your server captures the payment with Capture Full Payment, or authorizes it with Auth to capture later. Use the checkout token, as for any other Afterpay order. The grantId request field is only for payments with a stored on-file grant, so don’t send the grant from this checkout.

Afterpay Button

This flow uses the Afterpay Button configuration instead of the merchant configuration:

Afterpay.setV3Configuration(
CheckoutV3Configuration(
shopDirectoryMerchantId: "your_merchant_id",
region: .US,
environment: .sandbox
)
)

Step 1: Create the checkout

Call checkoutV3WithCashAppPay with the customer and order total. Store the result.

Afterpay.checkoutV3WithCashAppPay(
consumer: Consumer(email: "customer@example.com"),
orderTotal: OrderTotal(total: 100, shipping: .zero, tax: .zero)
) { result in
switch result {
case .success(let payload):
self.payload = payload
// Create the customer request with payload.cashAppSigningData.
case .cancelled(let reason):
// Signing failed. Hide Cash App Pay or start again.
print("Signing failed: \(reason)")
case .failure(let error):
// Creating the checkout failed.
print("Checkout failed: \(error)")
}
}

Step 2: Create Pay Kit and the customer request

Create Pay Kit with Afterpay.checkoutV3CashAppClientId, which reads the Afterpay Button configuration. For the endpoint, use the same environment that you passed to CheckoutV3Configuration: Afterpay.environment reads the merchant configuration, so it’s nil if you set only the Afterpay Button configuration. Then create the customer request with the signing data from the payload:

let signingData = payload.cashAppSigningData
payKit?.createCustomerRequest(
params: CreateCustomerRequestParams(
actions: [
.oneTimePayment(
scopeID: signingData.brandId,
money: Money(amount: signingData.amount, currency: .USD)
),
],
// Your app's redirect URL from "Handle the redirect".
redirectURL: URL(string: "yourapp://cashapp")!,
referenceID: nil,
metadata: nil
)
)

When Pay Kit moves to readyToAuthorize and the customer taps the Cash App Pay button, call authorizeCustomerRequest(_:).

Step 3: Confirm the checkout

When Pay Kit moves to approved, confirm the checkout with the grant. The SDK returns the virtual card details and the time before which you must authorize the card.

Afterpay.checkoutV3ConfirmForCashAppPay(
token: payload.token,
singleUseCardToken: payload.singleUseCardToken,
cashAppPayCustomerID: grant.customerID,
cashAppPayGrantID: grant.id,
jwt: payload.cashAppSigningData.jwt
) { result in
switch result {
case .success(let response):
// Process response.paymentDetails before response.cardValidUntil.
break
case .failure(let error):
// Handle the error.
break
}
}

Test Cash App Pay

In the sandbox, test with the Cash App sandbox app. Make sure that Pay Kit and the Afterpay SDK use the same environment.