From 720d8d6ad5fc28557e5581d4e0d7772430ce3d04 Mon Sep 17 00:00:00 2001 From: Mark Mroz Date: Tue, 6 Aug 2024 10:28:47 -0400 Subject: [PATCH] Added docs for CheckoutV3 and CheckoutV3 with CAP --- README.md | 20 -- docs/src/getting-started/cash-app-pay-v3.md | 263 ++++++++++++++++++++ docs/src/getting-started/cash-app-pay.md | 2 +- docs/src/getting-started/checkout-v3.md | 95 +++++++ 4 files changed, 359 insertions(+), 21 deletions(-) create mode 100644 docs/src/getting-started/cash-app-pay-v3.md create mode 100644 docs/src/getting-started/checkout-v3.md diff --git a/README.md b/README.md index 9eddfb9b..e92aa215 100644 --- a/README.md +++ b/README.md @@ -11,26 +11,6 @@ # Documentation Documentation for usage can be found [here][docs], including the [getting started][docs-getting-started] guide and [UI component][docs-ui] docs. -# Checkout V3 - -```swift -Afterpay.presentCheckoutV3Modally(over:consumer:total:items:animated:configuration:requestHandler:completion:) -``` - -Checkout version 3 returns a unique single use card for you to use in your existing checkout flow. - -The configuration object may be set using `setV3Configuration`, or passed into the checkout call. - -## Configuration - -```swift -Afterpay.fetchMerchantConfiguration(configuration:requestHandler:completion:) -``` - -As v3 removes the need for merchant integration with the Afterpay API, the `Configuration` — providing information about minimum and maximum order amounts — is now available through the SDK. - -The configuration object may be set using `setV3Configuration`, or passed into the checkout call. - # Contributing Contributions are welcome! Please read our [contributing guidelines][contributing]. diff --git a/docs/src/getting-started/cash-app-pay-v3.md b/docs/src/getting-started/cash-app-pay-v3.md new file mode 100644 index 00000000..b51323c5 --- /dev/null +++ b/docs/src/getting-started/cash-app-pay-v3.md @@ -0,0 +1,263 @@ +--- +layout: default +title: Cash App Pay with Checkout V3 +parent: Getting Started +nav_order: 6 +--- + +# Cash App Pay with Checkout V3 +{: .d-inline-block .no_toc } +NEW (v5.7.0) +{: .label .label-green } + + +
+ + Table of contents + + {: .text-delta } +- TOC +{:toc} +
+ + +{: .alert } +> Cash App Pay is currently available in the following region(s): US + +This method requires importing and implementing the Cash App PayKit SDK. + +## 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 PayKit 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) { + if let url = URLContexts.first?.url { + NotificationCenter.default.post( + name: CashAppPay.RedirectNotification, + object: nil, + userInfo: [UIApplication.LaunchOptionsKey.url : url] + ) + } + } +} +``` + +## Step 4: Configure Afterpay for V3 Checkouts + +Set the V3 configuration as follows: +```swift +Afterpay.setV3Configuration( + CheckoutV3Configuration( + shopDirectoryMerchantId: "merchant_id", + region: .US, + environment: .production + ) +) +``` + +## Step 5: Perform V3 Checkout + +Call `checkoutV3WithCashAppPay` and store the results: +```swift +Afterpay.checkoutV3WithCashAppPay( + consumer: Consumer(email: "example@email.com"), + orderTotal: OrderTotal(total: 100, shipping: .zero, tax: .zero) + ) { 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 + } + } +``` + +## Step 6: Instantiate Pay Kit iOS + +When you’re ready to authorize a payment using Cash App Pay, + +1. Instantiate the SDK with the Afterpay Cash App Pay Client ID via `Afterpay.checkoutV3CashAppClientId`. +2. The SDK defaults to point to the production endpoint; for development, set the endpoint to `.sandbox`. +3. Register your observer to the Pay Kit SDK. + +{: .note } +> Ensure that the Afterpay SDK is configured per Step 4 before attempting to access `Afterpay.checkoutV3CashAppClientId`. + +For example, from your checkout view controller that implements the `PayKitObserver` protocol, you might instantiate the SDK to be: + +``` swift +private let sandboxClientID = Afterpay.checkoutV3CashAppClientId +private lazy var sdk: CashAppPay = { + let sdk = CashAppPay(clientID: sandboxClientID, endpoint: .sandbox) + sdk.addObserver(self) + return sdk +}() +``` +## Step 7: 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. + +To charge a one-time payment, your **Create Request** call might look like this (in the following example, `checkoutV3` is the response object that is returned in the `checkoutV3WithCashAppPay` parameter from step 5): + +``` 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 8: Authorize the Customer Request + +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 9: Confirm the Cash App Pay Order + +Back in Afterpay, confirm the payment using the `checkoutV3` data from step 5 and the `customerRequest.grant` from step 8: + +```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 Card Details and the date by which the card must be used + case .failure(let failure): + // Handle failure + } + } +``` + + +## 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' + } + } +}%% + +sequenceDiagram + participant App + participant Afterpay SDK + participant Cash App Pay SDK + 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
as Afterpay SDK config + App->>App: Implement
deep linking + App->>Cash App Pay SDK: Register for state updates + App->>Afterpay SDK: Create a Checkout + Afterpay SDK->>Afterpay API: Creates a Vistual Card + Afterpay API-->>App: Checkout result with
Single Use Payment Card + Note over App,Afterpay API: Create Customer Request and Capture + App->>Cash App Pay SDK: Create a customer request + App->>Cash App Pay SDK: Authorize the customer request + App->>App: Handle payment
capture response +``` + +[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 \ No newline at end of file diff --git a/docs/src/getting-started/cash-app-pay.md b/docs/src/getting-started/cash-app-pay.md index ab270ea3..824fe66c 100644 --- a/docs/src/getting-started/cash-app-pay.md +++ b/docs/src/getting-started/cash-app-pay.md @@ -2,7 +2,7 @@ layout: default title: Cash App Pay parent: Getting Started -nav_order: 4 +nav_order: 5 --- # Cash App Pay diff --git a/docs/src/getting-started/checkout-v3.md b/docs/src/getting-started/checkout-v3.md new file mode 100644 index 00000000..b5313411 --- /dev/null +++ b/docs/src/getting-started/checkout-v3.md @@ -0,0 +1,95 @@ +--- +layout: default +title: Checkout V3 +parent: Getting Started +nav_order: 4 +--- +# Checkout V3 +{: .no_toc } +
+ + Table of contents + + {: .text-delta } +- TOC +{:toc} +
+ +{: .alert } +> Checkout V3 is currently available in the following region(s): US, UK, Australia and Canada + +Checkout Version 3 generates a one-time payment card for every Afterpay order and provides the card number to insert into your credit card checkout. This allows for a front-end-only integration. The one-time payment card is Visa for the US, UK, and Canada. In Australia the one-time payment card is Mastercard. + +## How it works + +The transaction uses a one-time virtual payment card, which has a unique card number. Once the virtual card exists, you use it to handle authorization, capture, and refunds. Your integration is simplified, as you don’t have to integrate with additional endpoints. + +{: .note } +Always set V3 Configuration before presentation, otherwise you will incur an assertionFailure. See the **Set Configuration** section below. + +## Set the V3 Configuration + +1. Set the configuration as follows: +```swift +Afterpay.setV3Configuration( + CheckoutV3Configuration( + shopDirectoryMerchantId: "your_merchant_id", + region: .US, + environment: .production + ) +) +``` +2. Present the checkout modally: +```swift +Afterpay.presentCheckoutV3Modally( + over: viewController, + consumer: Consumer(email: "example@email.com"), + orderTotal: OrderTotal(total: 100, shipping: .zero, tax: .zero), + items: [ Product(name: "coffee") ] +) { result in + switch result { + case .success(data: let data): + /// The virtual card details + /// The time before which an authorization needs to be made on the virtual card. + /// The collection of tokens required to update the merchant reference or cancel the virtual card + case .canceled(reason: let reason): + // Handle checkout cancellation + } +} +``` +3. Optionally, if you do not have an order number (merchantReference) at the time of launching the Afterpay V3, you will use the `Afterpay.updateMerchantReferenceV3` method to associate the order with the tokens. +```swift + /// Updates Afterpay's merchant reference for the transaction represented by the provided `tokens`. + /// - Parameters: + /// - merchantReference: A unique ID identifying the transaction. + /// - tokens: The set of tokens returned after a successful call to `Afterpay.presentCheckoutV3Modally`. + Afterpay.updateMerchantReferenceV3( + with: "merchantReference", + tokens: data.tokens + ) { [weak self] result in + switch result { + case .success: + // This endpoint returns a 204, so no response body + case .failure(let error): + // Handle failure + } + } +``` + +## Sequence Diagram +The below diagram describes the happy path. +```mermaid +sequenceDiagram + participant App + participant Afterpay SDK + participant Afterpay API + Note over App,Afterpay API: Setup + App->>Afterpay SDK: Configure the SDK + App->>Afterpay SDK: Launch the checkout + Note over App,Afterpay API: Consumer confirms Afterpay checkout + Afterpay SDK-->>App: Checkout result + 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 +``` \ No newline at end of file