Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions docs/preview/in-person/cards/guide-accept.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,45 @@ This section only applies to **merchants** accepting card payments.
Visit the [card payments section](../../../topics/payments/cards/index.mdx) for information about payments made with cards *from* a Swan account.
:::

## Step 1: Request a merchant profile {#step-1}
## Step 1: Set up the development environment {#step-1}

Install the Stripe Terminal SDK and configure your app with the required Apple entitlements and permissions.

→ [Set up the development environment](./guide-setup.mdx)

## Step 2: Request a merchant profile {#step-2}

Before accepting in-person card payments, you need a merchant profile.

→ [Request a merchant profile](../../../topics/merchants/profiles/guide-request.mdx)
:::caution Early access: manual profile setup
During this phase, Swan manually sets up merchant profiles with the Stripe connection.
After requesting a profile, contact your Product Integration Manager with your merchant profile ID.
They will confirm when setup is complete and provide your `$LOCATION_ID`.
In a future release, the location ID will be available as `mainLocationId` on the `InPersonCardMerchantPaymentMethod` object.
Swan can configure up to 10 profiles per partner during early access.
:::

You can also update merchant profiles if needed.
→ [Request a merchant profile](../../../topics/merchants/profiles/guide-request.mdx)

→ [Update a merchant profile](../../../topics/merchants/profiles/guide-update.mdx)
## Step 3: Request the payment method {#step-3}

## Step 2: Request the payment method {#step-2}
Request the in-person card payment method to enable your merchants to accept card payments.

→ [Request the in-person card payment method](./guide-request-method.mdx)

## Step 3: Create in-person card payments {#step-3}
## Step 4: Initialize the device {#step-4}

Set up the connection token provider and connect your device to the Stripe Terminal reader.

→ [Initialize the device for payments](./guide-initialize.mdx)

## Step 5: Create in-person card payments {#step-5}

Call the API to create payment intents.
This lets merchants accept card payments from customers using their terminal app.
Call the API to create payment intents and use the Stripe SDK to process payments.

→ [Create in-person card payments](./guide-create-payments.mdx)

## Step 4: Issue a refund {#step-4}
## Step 6: Issue a refund {#step-6}

import InPersonCardRefundLimitation from '../partials/_in-person-card-refund-limitation.mdx';

Expand Down
239 changes: 186 additions & 53 deletions docs/preview/in-person/cards/guide-create-payments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,12 @@ Create payment intents so your merchants can accept card payments from customers

## Prerequisites {#prerequisites}

- The [Stripe Terminal SDK](./guide-setup.mdx) installed and the [device initialized](./guide-initialize.mdx).
- A **merchant profile** with the status **Enabled**.
- An **in-person card payment method** with the status **Enabled**. If you don't have one yet, [request the payment method](./guide-request-method.mdx) first.
- A project access token, or a user access token with **Can manage members** rights.
- The [Stripe Terminal SDK](https://docs.stripe.com/terminal/payments/setup-sdk) integrated in your terminal app.

## Step 1: Get a connection token {#connection-token}

Get a connection token from Stripe so the terminal app can discover and connect to a reader

Call the `requestTerminalConnectionToken` mutation from your server.
Then, pass the returned `connectionToken` to the `StripeTerminalProvider` in your terminal app.

```graphql
mutation GetConnectionToken {
requestTerminalConnectionToken(
input: {
merchantProfileId: "$YOUR_MERCHANT_PROFILE_ID"
}
) {
... on RequestInPersonTerminalConnectionTokenSuccessPayload {
connectionToken
}
}
}
```

:::info Token lifecycle
Connection tokens are short-lived.
Your app should request a new token each time the Stripe SDK needs one, typically by providing a token fetch function to the `StripeTerminalProvider`.
:::

## Step 2: Create a payment intent {#create-intent}
## Step 1: Create a payment intent {#create-intent}

Call the `createInPersonPaymentIntent` mutation to create a payment intent.
The mutation returns a `secret` (Stripe's client secret) that you'll use in the next step with the Stripe SDK.
Expand All @@ -63,66 +37,225 @@ mutation CreateInPersonPayment {
__typename
message
}
... on AmountTooSmallRejection {
... on NotFoundRejection {
__typename
message
}
... on InternalErrorRejection {
__typename
message
}
... on ValidationRejection {
__typename
message
}
}
}
```

### Mutation input {#mutation-input}
### Mutation fields {#mutation-input}

<FieldRequirementsLegend />

| Field | Type | Required | Description |
| Field | Type | | Description |
| --- | --- | --- | --- |
| `merchantProfileId` | `ID` | | ID of the merchant profile accepting the payment. |
| `amount` | `AmountInput` | | Amount for the payment intent. Minimum: €0.50. |
| `label` | `String` | No | Label shown on the merchant's bank statement. |
| `externalReference` | `String` | No | Reference to match the payment with your external system (for example, an order ID). |
| `reference` | `String` | No | Payment reference. |
| `idempotencyKey` | `String` | No | Unique key to prevent duplicate payment creation. |
| `merchantProfileId` | `ID` | <Req /> | ID of the merchant profile accepting the payment. |
| `amount` | `AmountInput` | <Req /> | Amount for the payment intent. Minimum: €0.50. |
| `label` | `String` | <Opt /> | Label shown on the merchant's bank statement. |
| `externalReference` | `String` | <Opt /> | Reference to match the payment with an external system, such as an order ID. |
| `reference` | `String` | <Opt /> | Payment reference. |
| `idempotencyKey` | `String` | <Opt /> | Unique key to prevent duplicate payment creation. |

## Step 3: Process the payment with Stripe SDK {#process-payment}
## Step 2: Process the payment with the Stripe SDK {#process-payment}

After creating the payment intent, use the Stripe Terminal SDK in your terminal app to collect and confirm the payment.
This step happens entirely on the device.
This step runs entirely on the device.

:::tip
For detailed SDK integration guidance, refer to the [Stripe Terminal: Collect card payment](https://docs.stripe.com/terminal/payments/collect-card-payment) documentation.
:::
### 2.1: Retrieve the payment intent {#retrieve-intent}

Use the `secret` from step 1 to retrieve the payment intent with the Stripe SDK.

<Tabs groupId="platform">
<TabItem value="react-native" label="React Native">

```tsx
const secret = "$PAYMENT_INTENT_SECRET"; // from step 1
const { paymentIntent, error } = await retrievePaymentIntent(secret);
if (error) {
// Handle error
return;
}
```

### 3.1: Retrieve the payment intent {#retrieve-intent}
</TabItem>
<TabItem value="ios" label="iOS">

Use the `secret` returned in step 2 to retrieve the payment intent with the Stripe SDK's `retrievePaymentIntent` method.
```swift
let secret = "$PAYMENT_INTENT_SECRET" // from step 1
Terminal.shared.retrievePaymentIntent(clientSecret: secret) { retrieveResult, retrieveError in
if let error = retrieveError {
print("retrievePaymentIntent failed: \(error)")
} else if let paymentIntent = retrieveResult {
// Proceed to collect payment method
}
}
```

</TabItem>
<TabItem value="android" label="Android">

```kotlin
val secret = "$PAYMENT_INTENT_SECRET" // from step 1
Terminal.getInstance().retrievePaymentIntent(
secret,
object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
// Proceed to collect payment method
}
override fun onFailure(e: TerminalException) {
// Handle error
}
}
)
```

### 3.2: Collect the payment method {#collect-method}
</TabItem>
</Tabs>

Call the Stripe SDK's `collectPaymentMethod` method.
### 2.2: Collect the payment method {#collect-method}

Call `collectPaymentMethod` to show the Tap to Pay screen.
This activates the device's NFC reader and prompts the customer to tap their card.

### 3.3: Confirm the payment intent {#confirm-intent}
<Tabs groupId="platform">
<TabItem value="react-native" label="React Native">

```tsx
const { paymentIntent, error } = await collectPaymentMethod({
paymentIntent: paymentIntent,
enableCustomerCancellation: true,
});
if (error) {
// Handle error
}
```

</TabItem>
<TabItem value="ios" label="iOS">

```swift
let collectConfig = try CollectConfigurationBuilder()
.setEnableCustomerCancellation(true)
.build()

self.collectCancelable = Terminal.shared.collectPaymentMethod(
paymentIntent: paymentIntent,
collectConfig: collectConfig
) { collectResult, collectError in
if let error = collectError {
print("collectPaymentMethod failed: \(error)")
} else if let paymentIntent = collectResult {
// Proceed to confirm
}
}
```

</TabItem>
<TabItem value="android" label="Android">

```kotlin
val cancelable = Terminal.getInstance().collectPaymentMethod(
paymentIntent,
object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
// Proceed to confirm
}
override fun onFailure(e: TerminalException) {
// Handle error
}
},
CollectConfiguration.Builder()
.setEnableCustomerCancellation(true)
.build()
)
```

</TabItem>
</Tabs>

### 2.3: Confirm the payment intent {#confirm-intent}

Call `confirmPaymentIntent` to finalize the payment.
The payment is authorized and captured automatically.

Call the Stripe SDK's `confirmPaymentIntent` method to finalize the payment.
The payment is captured automatically.
:::tip Retrying a failed capture
If capture fails and you want to offer a retry (for example, with a different card), reuse the same payment intent.
Repeat step 2.2, then attempt to confirm again.
:::

<Tabs groupId="platform">
<TabItem value="react-native" label="React Native">

```tsx
const { paymentIntent, error } = await confirmPaymentIntent({
paymentIntent: paymentIntent,
});
if (error) {
// Handle error
}
```

</TabItem>
<TabItem value="ios" label="iOS">

```swift
self.confirmCancelable = Terminal.shared.confirmPaymentIntent(
paymentIntent
) { confirmResult, confirmError in
if let error = confirmError {
print("confirmPaymentIntent failed: \(error)")
} else if let confirmedPaymentIntent = confirmResult {
print("confirmPaymentIntent succeeded")
}
}
```

</TabItem>
<TabItem value="android" label="Android">

```kotlin
val cancelable = Terminal.getInstance().confirmPaymentIntent(
paymentIntent,
object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
// Payment confirmed
}
override fun onFailure(e: TerminalException) {
// Handle error
}
}
)
```

</TabItem>
</Tabs>

## Merchant payment statuses {#statuses}

After processing, the merchant payment object reflects the payment's lifecycle.
Refer to the [payment object statuses](./index.mdx#payment-statuses) for the full list.

These are the key statuses after a payment is processed:

| Status | Explanation |
| --- | --- |
| `Captured` | Payment authorized and captured successfully. Funds will be [settled](./index.mdx#settlement) to the merchant's account. |
| `Captured` | Payment authorized and captured. Funds will be [settled](./index.mdx#settlement) to the merchant's account. |
| `Rejected` | Payment declined by the issuer or by Swan. Refer to [rejection reasons](./index.mdx#rejected) for details. |
| `Disputed` | Customer disputed the payment for some or all of the amount. |
| `Refunded` | Payment reversed by the merchant for some or all of the amount. |

## After the payment {#after-payment}

After the payment is captured, the merchant payment object is updated and underlying transactions are created.
Refer to the [card transaction types](./index.mdx#transaction-types) section to understand how transactions are created and settled.
Refer to [card transaction types](./index.mdx#transaction-types) to understand how they settle.

import InPersonCardRefundLimitation from '../partials/_in-person-card-refund-limitation.mdx';

Expand Down
Loading