> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gr4vy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Adyen - Cash App Afterpay

> Configure Cash App Afterpay via Adyen as a payment method in Gr4vy.

export const connector = {
  displayName: "Cash App Afterpay",
  method: "cashappafterpay",
  features: "create_transaction deep_linking direct_capture direct_integration_create partial_refunds redirect_requires_popup refunds requires_webhook_setup settlement_reporting settlement_reporting_from_webhook verify_credentials",
  supportedCountries: "US",
  supportedCurrencies: "USD"
};

export const ConnectorRegions = ({data, kind, name: nameOverride}) => {
  const [query, setQuery] = useState("");
  const [open, setOpen] = useState(false);
  const isCountries = kind === "countries";
  const raw = data && (isCountries ? data.supportedCountries : data.supportedCurrencies);
  const codes = typeof raw === "string" ? raw.split(/\s+/).filter(Boolean) : Array.isArray(raw) ? raw : [];
  const DISPLAY_NAME_OVERRIDES = {
    authorizenet: "Authorize.net",
    cardpointe: "Fiserv CardPointe",
    dlocal: "dLocal",
    shift4i4go: "Shift4 i4go",
    tokenex: "TokenEx"
  };
  const rawName = data && data.displayName || "";
  const name = nameOverride || DISPLAY_NAME_OVERRIDES[rawName.toLowerCase()] || rawName || "This connector";
  const verb = isCountries ? "supports transactions from buyers in" : "supports processing payments in";
  const noun = isCountries ? "countries" : "currencies";
  if (codes.length === 0) return null;
  let displayNames = null;
  try {
    displayNames = new Intl.DisplayNames(["en"], {
      type: isCountries ? "region" : "currency"
    });
  } catch (e) {
    displayNames = null;
  }
  const resolve = code => {
    if (!displayNames) return null;
    try {
      const resolved = displayNames.of(code);
      return resolved && resolved !== code ? resolved : null;
    } catch (e) {
      return null;
    }
  };
  const MAJOR_CURRENCIES = ["USD", "EUR", "GBP", "CAD", "AUD", "JPY", "CHF", "CNY", "SGD", "HKD", "NZD", "SEK", "NOK", "DKK", "MXN", "BRL", "INR"];
  const items = codes.map(code => ({
    code,
    label: resolve(code)
  }));
  if (isCountries) {
    items.sort((a, b) => (a.label || a.code).localeCompare(b.label || b.code));
  } else {
    const rank = code => {
      const i = MAJOR_CURRENCIES.indexOf(code);
      return i === -1 ? MAJOR_CURRENCIES.length : i;
    };
    items.sort((a, b) => rank(a.code) - rank(b.code) || a.code.localeCompare(b.code));
  }
  if (codes.length <= 3) {
    const parts = items.map(it => isCountries || !it.label ? it.label || it.code : `${it.label} (${it.code})`);
    const joined = parts.length === 1 ? parts[0] : parts.length === 2 ? `${parts[0]} and ${parts[1]}` : `${parts.slice(0, -1).join(", ")}, and ${parts[parts.length - 1]}`;
    return <p>
        {name} {verb} {joined}.
      </p>;
  }
  const chipStyle = {
    display: "inline-flex",
    alignItems: "baseline",
    gap: "0.4rem",
    padding: "0.15rem 0.55rem",
    borderRadius: "0.375rem",
    border: "1px solid rgba(128, 128, 128, 0.25)",
    fontSize: "0.875rem",
    lineHeight: 1.5
  };
  const codeStyle = {
    fontFamily: "var(--font-mono, ui-monospace, monospace)",
    fontWeight: 600,
    fontSize: "0.8125rem"
  };
  const controlStyle = {
    color: "inherit",
    background: "transparent",
    border: "1px solid rgba(128, 128, 128, 0.3)",
    borderRadius: "0.5rem",
    fontSize: "0.875rem"
  };
  const renderChip = it => <span key={it.code} style={chipStyle} title={isCountries ? it.code : it.label || it.code}>
      {isCountries ? it.label || it.code : <span style={codeStyle}>{it.code}</span>}
      {!isCountries && it.label ? <span style={{
    opacity: 0.7
  }}>{it.label}</span> : null}
    </span>;
  const PREVIEW = 5;
  const collapsible = items.length > PREVIEW;
  const q = query.trim().toLowerCase();
  const filtered = q ? items.filter(it => it.code.toLowerCase().includes(q) || it.label && it.label.toLowerCase().includes(q)) : items;
  const expanded = open || q !== "";
  const visible = !collapsible ? items : expanded ? filtered : items.slice(0, PREVIEW);
  const toggle = () => {
    const next = !open;
    setOpen(next);
    if (!next) setQuery("");
  };
  return <div>
      <p>
        {name} {verb} the following {codes.length} {noun}:
      </p>

      {collapsible ? <input type="text" value={query} onChange={e => setQuery(e.target.value)} placeholder={`Filter ${noun}…`} aria-label={`Filter ${noun}`} style={{
    ...controlStyle,
    display: "block",
    width: "100%",
    maxWidth: "22rem",
    padding: "0.4rem 0.7rem",
    margin: "0 0 0.75rem"
  }} /> : null}

      <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.4rem"
  }}>
        {visible.map(renderChip)}
      </div>

      {q && filtered.length === 0 ? <p style={{
    opacity: 0.7,
    marginTop: "0.6rem"
  }}>
          No {noun} match “{query.trim()}”.
        </p> : null}
      {q && filtered.length > 0 ? <p style={{
    opacity: 0.6,
    fontSize: "0.8125rem",
    marginTop: "0.6rem"
  }}>
          Showing {filtered.length} of {items.length}.
        </p> : null}

      {collapsible && !q ? <button type="button" aria-expanded={open} onClick={toggle} style={{
    ...controlStyle,
    display: "inline-flex",
    alignItems: "center",
    gap: "0.4rem",
    padding: "0.35rem 0.75rem",
    marginTop: "0.75rem",
    cursor: "pointer"
  }}>
          <span aria-hidden="true" style={{
    display: "inline-block",
    transform: open ? "rotate(90deg)" : "none",
    transition: "transform 0.15s ease"
  }}>
            ›
          </span>
          {open ? "Show fewer" : `and ${items.length - PREVIEW} more`}
        </button> : null}
    </div>;
};

export const ConnectorCapabilities = ({data}) => {
  const CAPABILITIES = [{
    keys: ["three_d_secure_pass_through"],
    label: "3-D Secure",
    description: "Gr4vy runs the 3DS authentication and sends the results to the provider on the authorization.",
    cardOnly: true
  }, {
    keys: ["three_d_secure_hosted"],
    label: "3-D Secure (provider-hosted)",
    description: "The provider runs the 3DS authentication itself, redirecting the buyer to its own page.",
    cardOnly: true
  }, {
    keys: ["partial_authorization"],
    label: "Partial authorization",
    description: "Support partial approval responses."
  }, {
    keys: ["zero_auth"],
    label: "Zero auth",
    description: "Verify a card without charging it."
  }, {
    keys: ["void"],
    label: "Void",
    description: "Cancel an authorized transaction before capture."
  }, {
    keys: ["direct_capture"],
    label: "Direct capture",
    description: "Capture a payment immediately at authorization.",
    hideWhenUnsupported: true
  }, {
    keys: ["delayed_capture"],
    label: "Delayed capture",
    description: "Authorize a payment and capture it at a later time."
  }, {
    keys: ["partial_capture"],
    label: "Partial capture",
    description: "Capture a portion of the authorized amount."
  }, {
    keys: ["over_capture"],
    label: "Over capture",
    description: "Capture more than the originally authorized amount."
  }, {
    keys: ["refunds"],
    label: "Refunds",
    description: "Refund a captured payment."
  }, {
    keys: ["partial_refunds"],
    label: "Partial refunds",
    description: "Refund a portion of the captured amount."
  }, {
    keys: ["settlement_reporting"],
    label: "Settlement reporting",
    description: "Automatic settlement and reconciliation reporting."
  }, {
    keys: ["create_session"],
    label: "Create session",
    description: "Create a connector session for client-side flows."
  }, {
    keys: ["network_tokens_default", "network_tokens_toggle"],
    label: "Network tokens",
    description: "Network-level tokenization for improved approval rates.",
    cardOnly: true
  }, {
    keys: ["digital_wallets"],
    label: "Digital wallets",
    description: "Apple Pay, Google Pay, and other wallet integrations."
  }, {
    keys: ["payment_method_tokenization", "payment_method_tokenization_toggle"],
    label: "Payment method tokenization",
    description: "Store payment methods outside of transactions."
  }, {
    keys: ["transaction_sync"],
    label: "Transaction sync",
    description: "Synchronize transaction state from the connector."
  }, {
    keys: ["create_token"],
    label: "Tokenization",
    description: "Create a token from card details collected via Secure Fields.",
    hideWhenUnsupported: true
  }, {
    keys: ["delete_token"],
    label: "Delete token",
    description: "Delete a stored token.",
    hideWhenUnsupported: true
  }, {
    keys: ["verify_credentials"],
    label: "Verify credentials",
    description: "Validate the configured credentials against the connector.",
    hideWhenUnsupported: true
  }];
  const raw = data && data.features;
  const enabled = typeof raw === "string" ? new Set(raw.split(/\s+/).filter(Boolean)) : Array.isArray(raw) ? new Set(raw) : new Set(Object.keys(raw || ({})).filter(key => raw[key]));
  const isOn = entry => entry.keys.some(key => enabled.has(key));
  const isNonCard = data && data.method && data.method !== "card";
  const renderGroup = (title, entries, supported) => {
    if (entries.length === 0) return null;
    const mark = supported ? "✓" : "✕";
    const markColor = supported ? "#16a34a" : "#9ca3af";
    return <div style={{
      marginTop: "1rem"
    }}>
        <div style={{
      fontSize: "0.75rem",
      fontWeight: 600,
      letterSpacing: "0.05em",
      textTransform: "uppercase",
      opacity: 0.6,
      marginBottom: "0.25rem"
    }}>
          {title}
        </div>
        {}
        <div role="list">
          {entries.map(entry => <div role="listitem" key={entry.label} style={{
      display: "flex",
      gap: "0.5rem",
      alignItems: "baseline",
      padding: "0.3rem 0",
      opacity: supported ? 1 : 0.7
    }}>
              <span aria-hidden="true" style={{
      color: markColor,
      fontWeight: 700,
      flexShrink: 0
    }}>
                {mark}
              </span>
              <span>
                <strong>{entry.label}</strong>
                {entry.description ? <span style={{
      opacity: 0.85
    }}> — {entry.description}</span> : null}
              </span>
            </div>)}
        </div>
      </div>;
  };
  const visible = isNonCard ? CAPABILITIES.filter(entry => !entry.cardOnly) : CAPABILITIES;
  const supported = visible.filter(isOn);
  const unsupported = visible.filter(entry => !isOn(entry) && !entry.hideWhenUnsupported);
  return <div>
      {renderGroup("Supported", supported, true)}
      {renderGroup("Not supported", unsupported, false)}
    </div>;
};

Adyen is a global payment technology company founded in 2006 in Amsterdam, Netherlands. The company provides a single platform for accepting payments across online, mobile, and in-store channels for major enterprises worldwide including Uber, Spotify, and Microsoft.

Cash App Afterpay is a buy now, pay later (BNPL) payment method offered by Block in the United States, where Afterpay is now part of Cash App. The buyer approves the purchase with Afterpay and pays for it in installments. Cash App Afterpay is known as Afterpay Touch in some regions, and Adyen processes it as the `afterpaytouch_US` payment method.

## Setup

Please follow the [common Adyen instructions](./adyen) to get set up with Cash App Afterpay.

Next, make sure to enable Cash App Afterpay as a payment method on your configured account.

## Supported countries

<ConnectorRegions data={connector} kind="countries" />

## Supported currencies

<ConnectorRegions data={connector} kind="currencies" />

## Capabilities

<ConnectorCapabilities data={connector} />

## Required fields

Cash App Afterpay requires the buyer's billing details and the cart items on every transaction. Include the following buyer fields when creating a transaction:

* `buyer.billing_details.first_name`
* `buyer.billing_details.last_name`
* `buyer.billing_details.email_address`
* `buyer.billing_details.address.line1`
* `buyer.billing_details.address.city`
* `buyer.billing_details.address.state`
* `buyer.billing_details.address.postal_code`
* `buyer.billing_details.address.country`

Also send `cart_items` with the transaction. Afterpay uses the cart to present the installment plan to the buyer, and the transaction fails when the cart is missing.

## Integration

For Cash App Afterpay, the default integration for Adyen is through a redirect to a hosted payments page.

### Redirect integration

Start by creating a new transaction with the payment method set to `cashappafterpay`, along with the buyer billing details and cart items listed in [Required fields](#required-fields).

<CodeGroup>
  ```csharp C# theme={"system"}
  var transaction = await client.Transactions.CreateAsync(
    transactionCreate: new TransactionCreate()
    {
      Amount = 1299,
      Currency = "USD",
      Country = "US",
      Intent = "capture",
      PaymentMethod =
        TransactionCreatePaymentMethod.CreateRedirectPaymentMethodCreate(
          new RedirectPaymentMethodCreate()
          {
            Method = "cashappafterpay",
            Country = "US",
            Currency = "USD",
            RedirectUrl = "https://example.com/callback",
          }
        ),
      Buyer = new GuestBuyer()
      {
        BillingDetails = new BillingDetails()
        {
          FirstName = "John",
          LastName = "Doe",
          EmailAddress = "john.doe@example.com",
          Address = new Address()
          {
            Line1 = "1 Market Street",
            City = "San Francisco",
            State = "CA",
            PostalCode = "94105",
            Country = "US",
          },
        },
      },
      CartItems = new List<CartItem>()
      {
        new CartItem()
        {
          Name = "Example Product",
          Quantity = 1,
          UnitAmount = 1299,
        },
      },
    }
  );
  ```

  ```go Go theme={"system"}
  amount := int64(1299)
  currency := "USD"
  country := "US"
  method := components.RedirectPaymentMethodCreateMethodCashappafterpay
  redirectUrl := "https://example.com/callback"

  redirectPaymentMethodCreate := components.RedirectPaymentMethodCreate{
    Method: method,
    Country: country,
    Currency: currency,
    RedirectURL: redirectUrl,
  }
  paymentMethod := components.CreateTransactionCreatePaymentMethodRedirectPaymentMethodCreate(redirectPaymentMethodCreate)

  buyer := components.GuestBuyer{
    BillingDetails: &components.BillingDetails{
      FirstName:    gr4vy.String("John"),
      LastName:     gr4vy.String("Doe"),
      EmailAddress: gr4vy.String("john.doe@example.com"),
      Address: &components.Address{
        Line1:      gr4vy.String("1 Market Street"),
        City:       gr4vy.String("San Francisco"),
        State:      gr4vy.String("CA"),
        PostalCode: gr4vy.String("94105"),
        Country:    gr4vy.String("US"),
      },
    },
  }

  transactionCreate := components.TransactionCreate{
    Amount:        amount,
    Currency:      currency,
    Country:       &country,
    Intent:       gr4vy.Pointer(components.TransactionIntentCapture),
    PaymentMethod: &paymentMethod,
    Buyer:         &buyer,
    CartItems: []components.CartItem{
      {
        Name:       "Example Product",
        Quantity:   1,
        UnitAmount: 1299,
      },
    },
  }

  transaction, err := client.Transactions.Create(ctx, transactionCreate, nil, nil, nil)
  ```

  ```java Java theme={"system"}
  CreateTransactionResponse transactionResponse = gr4vyClient.transactions().create()
    .transactionCreate(TransactionCreate.builder()
      .amount(1299L)
      .currency("USD")
      .country("US")
      .intent(TransactionIntent.CAPTURE)
      .paymentMethod(TransactionCreatePaymentMethod.of(RedirectPaymentMethodCreate.builder()
        .method(RedirectPaymentMethodCreateMethod.CASHAPPAFTERPAY)
        .country("US")
        .currency("USD")
        .redirectUrl("https://example.com/callback")
        .build()))
      .buyer(GuestBuyer.builder()
        .billingDetails(BillingDetails.builder()
          .firstName("John")
          .lastName("Doe")
          .emailAddress("john.doe@example.com")
          .address(Address.builder()
            .line1("1 Market Street")
            .city("San Francisco")
            .state("CA")
            .postalCode("94105")
            .country("US")
            .build())
          .build())
        .build())
      .cartItems(List.of(CartItem.builder()
        .name("Example Product")
        .quantity(1L)
        .unitAmount(1299L)
        .build()))
      .build())
    .call();

  Transaction transaction = transactionResponse.transaction().orElse(null);
  ```

  ```php PHP theme={"system"}
  $transactionCreate = new TransactionCreate(
    amount: 1299,
    currency: 'USD',
    country: 'US',
    intent: 'capture',
    paymentMethod: new RedirectPaymentMethodCreate(
      method: 'cashappafterpay',
      country: 'US',
      currency: 'USD',
      redirectUrl: 'https://example.com/callback'
    ),
    buyer: new GuestBuyer(
      billingDetails: new BillingDetails(
        firstName: 'John',
        lastName: 'Doe',
        emailAddress: 'john.doe@example.com',
        address: new Address(
          line1: '1 Market Street',
          city: 'San Francisco',
          state: 'CA',
          postalCode: '94105',
          country: 'US'
        )
      )
    ),
    cartItems: [
      new CartItem(
        name: 'Example Product',
        quantity: 1,
        unitAmount: 1299
      ),
    ]
  );
  $response = self::$sdk->transactions->create($transactionCreate);
  $transaction = $response->transaction;
  ```

  ```python Python theme={"system"}
  transaction: models.Transaction = client.transactions.create(
    amount=1299,
    currency="USD",
    country="US",
    intent="capture",
    payment_method={
      "method": "cashappafterpay",
      "country": "US",
      "currency": "USD",
      "redirect_url": "https://example.com/callback",
    },
    buyer={
      "billing_details": {
        "first_name": "John",
        "last_name": "Doe",
        "email_address": "john.doe@example.com",
        "address": {
          "line1": "1 Market Street",
          "city": "San Francisco",
          "state": "CA",
          "postal_code": "94105",
          "country": "US",
        },
      }
    },
    cart_items=[
      {
        "name": "Example Product",
        "quantity": 1,
        "unit_amount": 1299,
      }
    ]
  )
  ```

  ```ts TypeScript theme={"system"}
  const transaction = await gr4vy.transactions.create({
    amount: 1299,
    currency: "USD",
    country: "US",
    intent: "capture",
    paymentMethod: {
      method: "cashappafterpay",
      country: "US",
      currency: "USD",
      redirectUrl: "https://example.com/callback"
    },
    buyer: {
      billingDetails: {
        firstName: "John",
        lastName: "Doe",
        emailAddress: "john.doe@example.com",
        address: {
          line1: "1 Market Street",
          city: "San Francisco",
          state: "CA",
          postalCode: "94105",
          country: "US"
        }
      }
    },
    cartItems: [
      {
        name: "Example Product",
        quantity: 1,
        unitAmount: 1299
      }
    ]
  })
  ```
</CodeGroup>

After the transaction is created, the API response includes `payment_method.approval_url` and the `buyer_approval_pending` status.

```json theme={"system"}
{
  "type": "transaction",
  "id": "ea1efdd0-20f9-44d9-9b0b-0a3d71e9b625",
  "payment_method": {
    "type": "payment-method",
    "approval_url": "https://cdn.sandbox.spider.gr4vy.app/connectors/adyen/apm.html?token=..."
  },
  "method": "cashappafterpay"
}
```

Redirect the buyer to the `approval_url` so they can approve the installment plan with Afterpay. After approval the buyer is redirected to the `redirect_url` you provided when creating the transaction. Do not rely solely on the redirect - either poll the transaction or (recommended) rely on webhooks to detect the final status (for example `capture_succeeded` or failure states).

### Direct integration

Adyen provides [web](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow?platform=Web\&integration=Components\&version=6.23.0), [Android](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow?platform=Android\&integration=Components\&version=5.15.0) and [iOS](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow?platform=iOS\&integration=Components\&version=5.21.1) SDKs for a direct integration. For these flows you should indicate the platform by setting an appropriate `integration_client` when creating the transaction, and then build a client-side integration that uses the [`POST /transactions/:transaction_id/session`](/reference/transactions/get-transaction-session) API to initialize the Adyen SDK.

For mobile, set `integration_client` to `ios` or `android` and use your app deep link for `redirect_url` (for example, `yourapp://`).

After the transaction is created, the API response includes a `session_token` which can be used to get the [session data](/reference/transactions/get-transaction-session) for that transaction.

```sh theme={"system"}
POST /transactions/:transaction_id/session?token=:session_token
```

This session data provides the `sessionId` and `sessionData` required to load the Adyen SDK. Refer to the [Cash App Pay direct integration](./adyen-cashapp#direct-integration) for a worked example of initializing the Adyen SDK with this session data.

## Testing

Adyen has [instructions](https://docs.adyen.com/payment-methods/cash-app-afterpay/web-component/#test-and-go-live) on how to test Cash App Afterpay.
