-
Notifications
You must be signed in to change notification settings - Fork 10
Doc 1585/ttp step 3 sandbox and rename #586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
max-koro
wants to merge
5
commits into
main
Choose a base branch
from
DOC-1585/ttp-step-3-sandbox-and-rename
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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. | ||||||
|
|
@@ -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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Simplify |
||||||
| 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'; | ||||||
|
|
||||||
|
|
||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.