Process Payments
On Mobile Card Readers
Now that you've authorized a card and received an encoded payment method, you're ready to process a Card Present Payment.
General Transaction Flows and Upcoming Changes
Flow | Current State | Future State | Progress |
---|---|---|---|
Approved auth -POST /payments results in decline | We return an HTTP 200 and will capture on the backend | N/A | N/A |
Refunds | A refund will fail if too much time has elapsed from the capture. The default refund limit is 60 days. We currently return an HTTP 200 with "failure_reason": "unknown" . | We return an HTTP 400, and identify any retryable refund errors to return HTTP 5XX. | Investigation |
Cancels | Cancel requests sent more than 90 minutes after the POST /payments request will result in an HTTP 400 - The payment will need to be captured and then refunded. | N/A | N/A |
Tipping | We do not validate the delta in amount between auth and capture. Generally, card networks will decline captures for greater than 120% of the authorization. | We will add validation to prevent capture requests with an amount greater than 120% of the authorization. In the meantime, your platform should either alert merchants or implement validation. | Planning |
Handle Tipping
- If Amount + Tip is less than to 120% of auth amount, then allow capture
- If Amount + Tip is greater than or equal to 120% of auth amount, then:
- Reverse the initial auth
- Obtain a new auth for the total amount (Amount + Tip)
Use the Encoded Payment Method
To create a payment, use the encoded payment method in thepayment_method.encoded_payment_method
parameter on the POST /payments
API request. Be sure to use the value as is, without adding any characters or spaces.Note
An encoded payment method can only be used once, and every Card Present transaction will need a newly-generated encoded payment method. That said, an encoded payment method will be valid for as long as the authorization. The hold on the cardholder's account will vary depending on the card issuer but the authorization is valid for 7 days.
POST /payments
request:- The merchant's Account ID (this must be the same Account ID used in the authorization function).
- The currency of the transaction (only USD is currently supported).
- The encoded payment method (returned by the Card Present SDK).
- The authorized amount (returned by the Card Present SDK).
Your request thus far would look like the following:
curl -X POST \
--url 'https://stage-api.wepay.com/payments' \
-H 'App-Id: {your-app-id}' \
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
-H 'Unique-Key: {guid}' \
--data-raw '{
"account_id": "{merchant's-account-id}",
"amount": "{desired-capture-amount}",
"fee_amount": {calculate-processing-and-platform-fees},
"currency”: “USD",
"payment_method": {
"encoded_payment_method": "{value-from-CP-SDK}""
}
}'
amount
amount
parameter in the POST /payments
request) should match the amount authorized. In order to capture a different amount, see the Capture Payments section below. Related information on capture scenarios can be found in the Delayed Capture section below as well.fee_amount
fee_amount
parameter values on Payments facilitated by your platform.To do this, find your rates in your contract and use those rates to calculate the processing fee per transaction. If your platform's business model relies on fees per transaction, this would be the place to add those fees in, as well.
Tipping
Tips provided by payers will need to be added to the authorized amount (either by the merchant from physical receipts, or automatically in your app's back end from your UI), and sent to us via the API. To successfully support tipping, use delayed capture to adjust the final amount by sending a request withauto_capture
either set to true
or not included. Here is an example with it set to true
:curl -X POST \
--url 'https://stage-api.wepay.com/payments' \
-H 'App-Id: {your-app-id}' \
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
-H 'Unique-Key: {guid}' \
--data-raw '{
"account_id": "{merchant's-account-id}",
"amount": "{desired-capture-amount}",
"currency": "USD",
"auto_capture": true,
"payment_method": {
"encoded_payment_method": "{value-from-CP-SDK}"
}
}'
Denomination
It is important to note that only newer versions of the Card Present SDK represent amounts in cents, just as the WePay API does.
Card Present SDK versions earlier than 10.0.0-prerelease-5 represent amounts in dollars while the WePay API represents amounts in cents. For instance, $1.00 in the Card Present SDK should be printed as 1.00, and in the API it must be converted to 100.
Handle 500 Errors
After a Card Present authorization returns from the SDK, the payer's portion of the transaction can be considered complete.
That said, it is possible to receive a 500 HTTP error on the subsequentPOST /payments
request. It is recommended to build out automated retry logic to handle 500's in the Card Present context, but it is up to your platform to design and implement the appropriate retry solution for your use case.In your design phase, take the following requirements into consideration:
- Use the same Unique-Key when re-trying a single transaction
- A single transaction is defined as the same payer, payment method, and purchase content
- This means that even if the payer comes back 5 minutes later to re-try the payment after you received a 500, the same Unique-Key should be used.
Provide Receipts
If a payer requests a receipt, the merchant must provide one.
For further receipt requirements, see the Card Present receipt guide, and the Card Present card network rules regarding receipts.Capture Payments
There are two overall methods of capturing payments with an encoded payment method:
Instant Capture
When callingPOST /payments
, the auto_capture
parameter defaults to true
even if it's not included in the request. This results in an instant capture of the Payment, which requires tips on receipts to be calculated into the total before calling POST /payments
.Asynchronous Capture
Once a card has been successfully authorized (resulting in an encoded payment method in the Card Present SDK response), aPOST /payments
does not need to happen right away. Authorizations are generally valid up to 7 days after the in-person transaction, though the exact timeframe depends on the card issuer.Risks:
- Auth expiration before a payment can be captured
- Lost auths due to inadequate logging
POST /payments
request immediately after an authorization with the relevant store and forward parameters (described further below), and then capture that payment later to help with logging encoded payment methods.Here is the high-level asynchronous capture flow:
The below use cases outline, at a high-level, different methods of implementing asynchronous capture.
Capture a Payment with no changes
- Capture method: manual or delayed.
- Manual: Send the
POST /payments/{id}/capture
request with no JSON body. - Delayed: Set the
capture_at
time in the initialPOST /payments
API request. The Payment will automatically capture at the designated time. - After 90 minutes, a capture is successful for a total equal to the authorization amount. No change in total authorized.
Capture a Payment with a total less than the authorized amount
- Capture method: partial.
- Partial: Send the
POST /payments/{id}/capture
request with the new total in theamounts
structure. Be sure to adjust the platform fees so that they do not exceed 20% of the total. - After 90 minutes and within 72 hours, a capture is successful for a total less than the authorized amount. The original authorized total is released from cardholder's account. A new authorized total for the lesser total is captured and posted to the cardholder's account.
- After 72 hours, a new authorization total is not placed against the cardholder's account.
Capture a Payment with a total greater than the authorized amount
- Capture method: manual or delayed.
- Manual: Send the
request with the new total in thePOST /payments/{id}/capture
amounts
structure. Adjust the platform fees as needed, and keep in mind that they cannot exceed 20% of the total. - Delayed: Set the
capture_at
time in the initialPOST /payments
request. Before the capture time, send aPOST /payments/{id}
request to update the amounts on the payment. Adjust fees as needed, and keep in mind that they cannot exceed 20% of the total. - After 90 minutes, a capture is successful for a total greater than the authorized amount. No change in total authorized.
High-Level Capture Flows
All capture flows begin with authorizing a card on the mobile card reader.
Manual Capture | Delayed Capture | Partial Capture | |
---|---|---|---|
|
|
|
|
Delayed Capture
Use delayed capture to create an API payment which will automatically capture at a designated time up to 7 days from authorization. To create a payment with delayed capture, send aPOST /payments
API request like this:curl -X POST \
--url 'https://stage-api.wepay.com/payments \
-H 'App-Id: {your-app-id}\
-H 'App-Token: {your-app-token}\
-H 'Api-Version: 3.0'\
-H 'Content-Type: application/json' \
--data-raw '{
"account_id": "{merchant's-account-id}",
"amount": "{desired-capture-amount}",
"capture_at": {calculate-UNIX-timestamp-within-7-days-of-current-time}
"fee_amount": {calculate-processing-and-platform-fees},
"currency”: “USD",
"payment_method": {
"encoded_payment_method": "{value-from-CP-SDK}"
}
}'
POST /payments
is March 18, 2020 at 3:29:29 PM PST. The farthest out that this payment can be captured would be March 25, 2020 at 3:29:29 PM PST. This means that the maximum UNIX timestamp that you can set as the value for capture_at
is 1585175369
.Manual Capture
Manual capture allows a card to be authorized and an API Payment to be created, but the final capture information can be different and the final capture time can be any time within 7 days of authorization.
The final capture amount can be greater than the authorized amount. Capturing an amount less than the authorized amount is considered partial capture. Data related to the payment can be updated, such as Level 2 & Level 3 data andcustom_data
. Payments using manual capture are typically dependent upon finalized information from the merchant or payer, which is why delayed capture would not be ideal for these payments. Manual capture allows the capture request to be sent as soon as payment details are finalized (i.e. shipping, tip, any changes to amount, or any changes to data), rather than waiting for the capture_at
time set in delayed capture.When creating a payment designated for manual capture, the API request will look like this:
curl -X POST \
--url 'https://stage-api.wepay.com/payments \
-H 'App-Id: {your-app-id}\
-H 'App-Token: {your-app-token}\
-H 'Api-Version: 3.0'\
-H 'Content-Type: application/json' \
--data-raw '{
"account_id": "{merchant's-account-id}",
"amount": "{desired-capture-amount}",
"auto_capture": false,
"fee_amount": {calculate-processing-and-platform-fees},
"currency”: “USD",
"payment_method": {
"encoded_payment_method": "{value-from-CP-SDK}""
}
}'
reference_id
, or any other data needs to be updated in addition to amounts, do so using a POST /payments/{id}
request:curl -X POST \
--url 'https://stage-api.wepay.com/payments/{id}' \
-H 'App-Id: {your-app-id}' \
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
--data-raw '{
"reference_id": "{new-reference-id}",
"amounts": {
"amount": {new-capture-amount},
"currency": "USD",
"fee_amount": {fee-amount}
}
}'
POST /payments/{id}/capture
request with the amounts
and/or custom_data
JSON blocks, or with no JSON body.With a JSON body:
curl -X POST \
--url 'https://stage-api.wepay.com/payments/{id}/capture' \
-H 'App-Id: {your-app-id}' \
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
--data-raw '{
"amounts": {
"amount": {new-capture-amount},
"currency": "USD",
"fee_amount": {fee-amount}
}
}'
Without a JSON body:
curl -X POST \
--url 'https://stage-api.wepay.com/payments/{id}/capture' \
-H 'App-Id: {your-app-id}' \
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
Partial Capture
Partial capture is a version of manual capture which allows a card to be authorized and an API Payment to be created, but the final amount in the manual capture request is less than the authorized amount.
To plan for partial capture scenarios, create Payments with aPOST /payments
request where the auto_capture
parameter is set to false
. The merchant should then have the option to update the amount for capture for 7 days after the API request. Once the merchant is satisfied, execute a payments/{id}/capture
call to finish. Your POST /payments
requests when partial capture is an option should look like this:curl -X POST \
--url 'https://stage-api.wepay.com/payments \
-H 'App-Id: {your-app-id}\
-H 'App-Token: {your-app-token}\
-H 'Api-Version: 3.0'\
-H 'Content-Type: application/json' \
--data-raw '{
"account_id": "{merchant's-account-id}",
"amount": "{desired-capture-amount}",
"auto_capture": false,
"fee_amount": {calculate-processing-and-platform-fees},
"currency”: “USD",
"payment_method": {
"encoded_payment_method": "{value-from-CP-SDK}""
}
}'
capture_at
time for the Payment within 7 days, and either set auto_capture
to true
or exclude that parameter from the request. There is no need to call /payments/{id}/capture
if you choose this flow.If the merchant opts to partially capture the Payment, then send a POST /payments/{id}/capture
request like this:curl -X POST \
--url 'https://stage-api.wepay.com/payments/{id}/capture' \
-H 'App-Id: {your-app-id}' \
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
--data-raw '{
"amounts": {
"amount": {new-capture-amount},
"currency": "USD",
"fee_amount": {fee-amount}
}
}'
fee_amount
parameter is required in the amounts
structure, and it must be no greater than 20% of the new capture amount.Note
POST /payments/{id}/capture
requests can only be made once per Payment. For partial capture, this means that you will not be able to later make another request to capture the full authorized amount.Manage Payment Operations
Partners leveraging our Clear solution must provide merchants with the tools to manage operations after a payment has been created.
Note
Partners leveraging our Link solution can direct merchants to their Chase dashboard for these tools.
Issue Refunds
Refund Card Present payments with the Refund API.Note that payments can only be refunded if they were created no more than 60 days prior to the time of refund. If your platform has a custom refund limit, that limit applies here.
For more in-depth guidance, see the Clear developer guide.Void Payments
Void Card Present payments with the Cancel API.pending
state and within 90 minutes of the authorization. After 90 minutes, the authorization will remain on the payer's card until it has expired.In order to construct the cancel API request, provide a UI-input for the merchant to submit the text to be used as the value for the cancel_reason
parameter.Your request may look something like this:
curl -X POST \
--url 'https://stage-api.wepay.com/payments/{id}/cancel' \
-H 'Accept: application/json' \
-H 'App-Id: {your-app-id}'\
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
--data-raw '{
"cancel_reason": "item(s) delayed - cannot fulfill order"
}'
A successful response will result in a voided payment.
Warning
Mobile payments can only be canceled within 90 minutes of the authorization.
To effectively release an authorization after an API Payment has been created and past the 90-minute window from authorization, take the following steps:
- Call
POST /payments/{id}
to update theamount
value to100
- Call
POST /payments/{id}/capture
within 72 hours from authorization request - Issue a refund for the remaining dollar with a
POST /refunds
request
Handle Disputes
Retaining signed receipts for Card Present payments is the best method to challenge any disputes a merchant may receive.
For more in-depth guidance, see the developer guide.On Terminals and SmartPOS
Now that you've authorized a card and received an encoded payment method, you're ready to process a Card Present Payment.
The iOS SDK now offers SDK Transaction Processing for Terminals, which allows you to create, capture, update, and cancel WePay Payments directly through the SDK instead of the WePay server API. This article walks through the server API flow, and the SDK Transaction Processing feature is outlined in the SDK package documentation. SDK Transaction processing documentation can be accessed by opening thedocumentation/html/SDK_version/index.html
file and finding the following sections of the Integration Guides tab:- Integrating the Capture functionality (terminal card readers only)
- Integrating the Update Deferred Capture functionality (terminal card readers only)
- Integrating the Cancel Authorization functionality (terminal card readers only)
- Integrating the Sale functionality (terminal card readers only)
General Transaction Flows and Upcoming Changes
Flow | Current State | Future State | Progress |
---|---|---|---|
Approved offline auth -POST /payments results in decline | It is possible for the capture of an offline authorization to be declined. This is a risk with accepting offline authorizations. | N/A | N/A |
Approved offline auth -POST /payments delay in processing | Occasionally, an HTTP 400 with will return on these when there is a delay in processing the auth on our backend. Note that this error can also occur if the request is attempted before the device has come back online. | Return an error to indicate steps that should be taken before the POST /payments request is retried. | Currently in development |
Approved online auth -POST /payments results in decline | We return an HTTP 200 and will capture on the backend | N/A | N/A |
Refund - Synchronous error | A refund will fail if too much time has elapsed from the capture. The default refund limit is 60 days. We currently return an HTTP 200 with "falure_reason": "unknown" . | We return an HTTP 400, and identify any retryable refund errors to return HTTP 5XX. | Investigation |
Refund - Asynchronous error | Note that is it possible for a refund request to result in an HTTP 200, but then asynchronously move to a failed state. | N/A. | N/A |
Cancels | POST /payments/{id}/cancel requests sent more than 90 minutes after the authorization initialized in the SDK will result in an HTTP 400 - The payment will need to be captured and then refunded. | N/A | N/A |
Tipping | We do not validate the delta in amount between auth and capture. Generally, card networks will decline captures for greater than 120% of the authorization. | We will add validation to prevent capture requests with an amount greater than 120% of the authorization. In the meantime, your platform should either alert merchants or implement validation. | Planning |
Handle Tipping
- If Amount + Tip is less than 120% of auth amount, then allow capture
- If Amount + Tip is greater than or equal to 120% of auth amount, then:
- Reverse the initial auth
- Obtain a new auth for the total amount (Amount + Tip)
Create a Payment
To create a payment, use the encoded payment method in thepayment_method.encoded_payment_method
parameter on the POST /payments
API request. Be sure to use the value as is, without adding any characters or spaces.Note
An encoded payment method can only be used once, and every Card Present transaction will need a newly-generated encoded payment method. That said, an encoded payment method will be valid for as long as the authorization. This amount of time will vary depending on the card issuer, but it will generally be 7 days.
POST /payments
request:- The merchant's Account ID (this must be the same Account ID which owns the Terminal).
- The currency of the transaction (only USD is currently supported).
- The encoded payment method (returned by the Card Present SDK).
- The authorized amount (returned by the Card Present SDK).
Your request thus far would look like the following:
curl -X POST \
--url 'https://stage-api.wepay.com/payments' \
-H 'App-Id: {your-app-id}' \
-H 'App-Token: {your-app-token}' \
-H 'Api-Version: 3.0' \
-H 'Content-Type: application/json' \
-H 'Unique-Key: {guid}' \
--data-raw '{
"account_id": "{merchant's-account-id}",
"amount": "{desired-capture-amount}",
"fee_amount": {calculate-processing-and-platform-fees},
"currency”: “USD",
"payment_method": {
"encoded_payment_method": "{value-from-CP-SDK}""
}
}'
amount
The amount captured (i.e. the value for the amount
parameter in the POST /payments
request) should match the amount authorized. In order to capture a different amount, see the Capture Payments section below. Related information on capture scenarios can be found in the Delayed Capture section below as well.fee_amount
We recommend charging, at minimum, our processing fees in this parameter. Remember that our processing fees are collected from your platform's WePay account, which collects funds based on the fee_amount parameter values on Payments facilitated by your platform.
To do this, find your rates in your contract and use those rates to calculate the processing fee per transaction. If your platform's business model relies on fees per transaction, this would be the place to add those fees in, as well.
Tipping
Tips provided on the receipt will need to be added to the total by the merchant and submitted to your platform via your merchant UI. This means that you should build a check specifically for terminals withtip.mode
set to prompt_on_receipt
, and use deferred capture to adjust the amount
once the merchant has submitted tip amounts at the end of the day.Tips provided on the terminal are included in the amount parameter of the authorization info object in the auth response from the Card Present SDK.
Denomination
It is important to note that the Card Present SDK represents amounts in dollars while the Payments API represents amounts in cents. For instance, $1.00 in the Card Present SDK should be printed as 1.00, and in the API it should be printed as 100.
Handle 500 Errors
After a Card Present authorization returns from the SDK, the payer's portion of the transaction can be considered complete.
That said, it is possible to receive a 500 HTTP error on the subsequentPOST /payments
request. It is recommended to build out automated retry logic to handle 500's in the Card Present context, but it is up to your platform to design and implement the appropriate retry solution for your use case.In your design phase, take the following requirements into consideration:
- Use the same Unique-Key when re-trying a single transaction
- A single transaction is defined as the same payer, payment method, and purchase content
- This means that even if the payer comes back 5 minutes later to re-try the payment after you received a 500, the same Unique-Key should be used.
Print Receipts
On the Verifone V400m terminal, merchants can print receipts from the device. Identify terminals with printer capabilities by examining the available printer flag in the Card Present SDK.
Verifone V400m terminals display an option for merchants to print a receipt after a successful authorization. If this option is selected, run the print receipt function in the Card Present SDK.
For further receipt requirements, see the Card Present receipt guide, and the Card Present card network rules regarding receipts.Signatures must be collected in the following cases:
- NFC/contactless transaction
- Keyed entry transaction
Additionally, merchants are encouraged to collect signature on receipt if they accept tip, and/or if they wish to validate the terms of sale (such as "all sales final").