Skip to content

Commit

Permalink
Update cash-app-pay-v3.md
Browse files Browse the repository at this point in the history
  • Loading branch information
mmroz authored Aug 12, 2024
1 parent 71dcdf7 commit 5dbfb34
Showing 1 changed file with 95 additions and 197 deletions.
292 changes: 95 additions & 197 deletions docs/src/getting-started/cash-app-pay-v3.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,35 @@
---
layout: default
title: Cash App Pay
title: Checkout V3
parent: Getting Started
nav_order: 4
nav_order: 4
---
# Cash App Pay
{: .d-inline-block .no_toc }
NEW (v5.3.0)
{: .label .label-green }
<details markdown="block">

# Checkout V3
{: .no_toc }

<details open markdown="block">
<summary>
Table of contents
</summary>
{: .text-delta }
- TOC
{:toc}
</details>
{: .alert }
> 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][afterpay-create-checkout-endpoint]{:target="_blank"} with a parameter of `isCashAppPay` set to `true`. This method requires importing and implementing the Cash App PayKit SDK.
{: .info }
> 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 ouput a malformed JSON error. The SDK’s example merchant server sets the parameters [here][example-server-props]{:target='_blank'}. See more details at [Redirect Method][api-reference-props]{:target='_blank'} in the Standard Checkout API.
## Step 1: Install the Cash App Pay Kit SDK
### Installation (Option One): SPM
You can install Pay Kit via SPM. Create a new Xcode project and navigate to `File > Swift Packages > Add Package Dependency`. Enter the URL `https://github.com/cashapp/cash-app-pay-ios-sdk` and tap **Next**. Choose the `main` branch, and on the next screen, download the required packages.
### Installation (Option Two): Cocoapods
Add Cocoapods to your project. Open the `Podfile` and add `pod 'CashAppPayKit'` and/or `pod 'CashAppPayKitUI'` and save your changes. Run `pod update` and Pay Kit will now be included through Cocoapods.
## Step 2: Implement the Cash App Pay Observer Protocol
To receive updates from Pay Kit, you’ll need to implement the Cash App Pay Observer protocol. Your checkout view controller can conform to this protocol, or you can create a dedicated observer class.
The `CashAppPayObserver` protocol contains only one method:
``` swift
func stateDidChange(to state: CashAppPaySDKState) {
// handle state changes
}
```
### States
Your implementation should switch on the `state` parameter and handle the appropriate state changes. Some of these possible states are for information only, but most drive the logic of your integration. The most critical states to handle are listed in the table below:
| State | Description |
|:------|:------------|
| `ReadyToAuthorize` | Show a Cash App Pay button in your UI and call `authorizeCustomerRequest()` when it is tapped. |
| `Approved` | Grants are ready for your backend to use 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. |
### Error States
| State | Description |
|:------|:------------|
| `.integrationError` | A fixable bug in your integration. |
| `.apiError` | A degradation of Cash App Pay server APIs. Your app should temporarily hide Cash App Pay functionality. |
| `.unexpectedError` | A bug outside the normal flow. Report this bug (and what caused it) to Cash App Developer Support. |
{: .info }
> You must update your UI in response to these state changes.
## Step 3: Implement URL handling
To use Pay Kit iOS, Cash App must be able to call a URL that will redirect back to your app. The simplest way to accomplish this is via [Custom URL Schemes][custom-url-schemes], but if your app supports [Universal Links][universal-links] you may use those URLs as well.
Choose a unique scheme for your application and register it in Xcode from the **Info** tab of your application’s Target.
You’ll pass a URL that uses this scheme (or a Universal Link your app handles) into the `createCustomerRequest()` method that starts the authorization process.
When your app is called back by Cash App, simply post the `CashAppPay.RedirectNotification` from your `AppDelegate` or `SceneDelegate`, and the SDK will handle the rest:
``` swift
import UIKit
import PayKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
NotificationCenter.default.post(
name: CashAppPay.RedirectNotification,
object: nil,
userInfo: [UIApplication.LaunchOptionsKey.url : url]
)
}
}
}
```
To set the v3 Configuration, use the settings in the code examples below.

Checkout version 3 allows you to load the checkout token on demand via `didCommenceCheckout` while presenting a loading view. It also supports `express` checkout features and callbacks which can either be handled in line or via a checkout handler object.

{: .note }
> You must meet two requirements to use checkout v3 successfully:
> - Always set Configuration before presentation, otherwise you will incur an assertionFailure. See the **Set Configuration** section below.
> - When you create a checkout token, set `popupOriginUrl` to `https://static.afterpay.com`. The SDK’s example merchant server sets the parameter [here][example-server-param]{:target="_blank"}. See more by checking the [API reference][express-checkout]{:target="_blank"}. Failing to do so will cause undefined behavior.
## Set the v3 Configuration

Use the settings in the code examples below.

1. Set the configuration as follows:

```swift
Afterpay.setV3Configuration(
CheckoutV3Configuration(
Expand All @@ -80,163 +39,102 @@ Afterpay.setV3Configuration(
)
)
```
2. Call "begin the checkout" and store the results:

2. Present the checkout modally:

```swift
Afterpay.checkoutV3WithCashAppPay(
Afterpay.presentCheckoutV3Modally(
over: viewController,
consumer: Consumer(email: "[email protected]"),
orderTotal: OrderTotal(total: 100, shipping: .zero, tax: .zero)
) { result in
orderTotal: OrderTotal(total: 100, shipping: .zero, tax: .zero),
items: [ Product(name: "test") ]
) { result in
switch result {
case .success(let checkoutV3):
// Store the token
// Store the singleUseCardToken
// Store the jwt, amount, redirectUri and brandID from the CashAppSigningData
case .cancelled(let reason):
// Handle reason
case .failure(let error):
// Handle error
case .success(data: let data):
// The Virtual Card details,
// time remaining for the authorization of the virtual card
// tokens required to update the merchant reference or cancel the virtual card
case .cancelled(reason: let reason):
// Handle checkout cancellation
}
}
```
## Step 4: Instantiate Pay Kit iOS
When you’re ready to authorize a payment using Cash App Pay,
1. Instantiate the SDK with the Afterpay Pay Kit Client ID via `Afterpay.cashAppClientId`.
2. The SDK defaults to point to the production endpoint; for development, set the endpoint to `.sandbox`.
3. Add your observer to the Pay Kit SDK.
{: .note }
> Ensure that the Afterpay SDK is configured per the [instructions][afterpay-configuration] before attempting to access `Afterpay.cashAppClientId`.
For example, from your checkout view controller that implements the `PayKitObserver` protocol, you might instantiate the SDK to be:
``` swift
private let sandboxClientID = Afterpay.cashAppClientId
private lazy var sdk: CashAppPay = {
let sdk = CashAppPay(clientID: sandboxClientID, endpoint: .sandbox)
sdk.addObserver(self)
return sdk
}()
```
## 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 must create this request as soon as your checkout view controller 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, the order must be signed, so that you can retrieve the JWT and associated data. This might look like the following example:
``` swift
let token = retrieveTokenFromServerToServerCall()
Afterpay.signCashAppOrderToken(token) { result in
switch result {
case .success(let cashData):
// TODO: use `cashData` to create customer request (step 5B)
case .failed(let reason):
// TODO: handle failed signing either by retrying with a new Cash App
// token or disabling / hiding the Cash App Pay button
}
}
```
### 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, `cashData` is the response object that is returned in the `didCommenceCheckout` parameter from step 5A):
``` swift
paykit.createCustomerRequest(
params: CreateCustomerRequestParams(
actions: [
.oneTimePayment(
scopeID: checkoutV3.cashAppSigningData.brandId,
money: Money(
amount: checkoutV3.cashAppSigningData.amount,
currency: .USD
)
),
],
channel: .IN_APP,
redirectURL: URL(string: checkoutV3.cashAppSigningData.redirectUri)!,
referenceID: nil,
metadata: nil
)
)
```
Your Observer’s state will change to `.creatingCustomerRequest`, then `.readyToAuthorize` with the created `CustomerRequest` struct as an associated value.
## Step 6: Authorize the Customer Request
### Step 6A: Add an Authorize Request Event to Cash App Pay button
Once the SDK is in the `.readyToAuthorize` state, you can store the associated `CustomerRequest` and display a Cash App Pay button. When the Customer taps the button, you can authorize the customer request. See [CashAppPayButton][cash-button]{:target='_blank'} to learn more about the Cash App Pay button component.
**Example**
``` swift
@objc func cashAppPayButtonTapped() {
sdk.authorizeCustomerRequest(request)
}
```
Your app will redirect to Cash App for authorization. When the authorization is completed, your redirect URL will be called and the `RedirectNotification` will post. Then, the SDK will fetch your authorized request and return it to your Observer, as part of the change to the `.approved` state.
### Step 6B: Validate the Cash App Pay Order
{: .alert }
This step must not be skipped
Back in Afterpay, confirm the order and confirm the payment:

The following examples are in Swift and UIKit. Make sure to adapt as needed for your specific implementation:

```swift
Afterpay.checkoutV3ConfirmForCashAppPay(
token: checkoutV3.token,
singleUseCardToken: checkoutV3.singleUseCardToken,
cashAppPayCustomerID: grant.customerID,
cashAppPayGrantID: grant.id,
jwt: checkoutV3.cashAppSigningData.jwt
) { result in
switch result {
case .success(let success):
// Recieve Payment Details and the date that the card is valid until
case .failure(let failure):
// Handle failure
import Afterpay

class MyViewController: UIViewController {
// You must maintain a reference to your handler as it is stored as a weak reference within the SDK
private let handler = CheckoutHandler()

override func viewDidLoad() {
super.viewDidLoad()

Afterpay.setCheckoutV3Handler(handler)
}

@objc func didTapPayWithAfterpay() {
Afterpay.presentCheckoutV3Modally(over: self) { result in
switch result {
case .success(let token):
// Handle successful Afterpay checkout
case .cancelled(let reason):
// Handle checkout cancellation
}
}
}
}
```

## Sequence Diagram

The below diagram describes the happy path.
``` mermaid
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#00c846',
'primaryTextColor': '#FFFFFF',
'primaryBorderColor': '#dfdfdf',
'signalTextColor': '#000000',
'signalColor': '#000000',
'secondaryColor': '#006100',
'tertiaryColor': '#fff'
}
}
}%%

```mermaid
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
App->>Afterpay SDK: Setup checkout handlers
Note over App,Afterpay API: Create checkout and Capture
App->>Proxy Server: Get Checkout 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`
Note over Proxy Server,Afterpay API: Ensure same environment<br>as Afterpay SDK config
Afterpay API-->>Proxy Server: Create Checkout Response
Note over Afterpay API,Proxy Server: Body contains a token
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
App->>Afterpay SDK: Launch the checkout<br>with the Token
Note over App,Afterpay API: Consumer confirms Afterpay checkout
Afterpay SDK-->>App: Checkout result
App->>Proxy Server: Capture request
Proxy Server->>Afterpay API: Capture request
Afterpay API-->>Proxy Server: Capture response
Proxy Server-->>App: Capture Response
App->>App: Handle response
```

[example-server-param]: https://github.com/afterpay/sdk-example-server/blob/5781eadb25d7f5c5d872e754fdbb7214a8068008/src/routes/checkout.ts#L28
[express-checkout]: https://developers.afterpay.com/afterpay-online/reference#what-is-express-checkout
```
[custom-url-schemes]: https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
[universal-links]: https://developer.apple.com/ios/universal-links/
[afterpay-configuration]: ../configuration
[cash-button]: https://cashapp-pay.stoplight.io/docs/api/technical-documentation/sdks/pay-kit/ios-getting-started#cashapppaybutton
[afterpay-create-checkout-endpoint]: https://developers.afterpay.com/afterpay-online/reference/create-checkout-1
[example-server-props]: https://github.com/afterpay/sdk-example-server/blob/5781eadb25d7f5c5d872e754fdbb7214a8068008/src/routes/checkout.ts#L26-L27
[api-reference-props]: https://developers.afterpay.com/afterpay-online/reference/javascript-afterpayjs#redirect-method

0 comments on commit 5dbfb34

Please sign in to comment.