Skip to main content

Stripe Payment Authorization and Capture

In the following recipe, we will walk through a flow where a shopper checks out on the storefront and their payment is authorized and then captured following the merchant's order validation. We will use Stripe and Elastic Path Commerce Cloud APIs as well as a Postman call to simulate a webhook event from an Order Management System (or similar).

Overall Orchestration Flow

Here is the flow that we will be modeling in the DX Engine:

  • A shopper checks out, providing payment and shipping information.
  • A Stripe Payment Intent is created to authorize the payment.
  • An Elastic Path order and authorization transaction are created.
  • The merchant validates the order, triggering a webhook with the order status.
  • A Conscia listener hears the event and triggers Stripe to capture the payment, and Elastic Path to update its transaction to captured and to complete the order.

alt_text

Mapping Out Orchestration Components, Templates, and Listeners

Translating this to Conscia’s Orchestration, the Checkout and Payment Authorization flow consists of the following Components and Context values in the Checkout Template:

alt_text

Then there is a Conscia Order Validated listener that triggers the Payment Capture flow, which consists of the following Components and Context values in the Capture Template:

alt_text

When the anonymous shopper completes checkout in the storefront after entering their shipping and payment details, the storefront sends off a query request to the DX Engine. It calls the Checkout Template and supplies the cartId along with the shopper's shipping information. The call looks like this:

POST {{engineUrl}}/experience/templates/_query  
X-Customer-Code: {{customerCode}}
Authorization: Bearer {{dxEngineToken}}

{
"templateCode": "epcc-guest-checkout",
"context": {
"cartId" : "5634ab34o1",
"email" : "barney@friends.com",
...
}
}

The Order Validated listener invokes the Capture Template once the merchant has validated the order and passes the necessary Context values through the engineRequest field on the listener:

"engineRequest": {
"templateCode": "epcc-capture-payment",
"context": {
"epcc_orderId": "`body.orderId`",
"status": "`body.status`"
}
}

Orchestration Flow

When the Checkout Template is called by the storefront, it triggers the Elastic Path checkout process well as the Stripe payment authorization.

  • Elastic Path Checkout Guest: The checkout call in Elastic Path automatically converts the cart into an order, and returns the new order information. This call uses an implicit token. (See the Add Product to Cart recipe for more information on how to configure an Elastic Path Get Implicit Token component).

  • Stripe Create PaymentIntent (Authorize Payment): This component makes a call to Stripe to create a payment intent and sets the amount owing from the Elastic Path order, the payment method to card, the payment capture method to manual, and confirmation to true. We can also add the Elastic Path order id as custom metadata to help track payments. The payment intent is then sitting in a state of requires_capture.

  • Elastic Path Initialize Transaction (Authorize Payment): Once the Stripe PaymentIntent is created, we can initialize an authorization transaction in Elastic Path to track payment through the manual gateway. The amount owing is set and the Stripe reference id is added also as custom metadata. This automatically changes the related Elastic Path order status to processing.

And that's where the flow sits until the merchant is able to verify the order on their end, perhaps allocate inventory, and do any other checks required to ensure they will be able to fulfill the order.

We're making the assumption that the merchant will manually mark the order verified in their order management system before the Stripe authorization expires in 7 days. The order management system will emit a webhook containing the order id and status.

In Conscia, we have created an Order Validated listener waiting for this order validated event. It's looking in particular for the order id and status, and then triggering the Elastic Path Capture Payment Template.

  • Elastic Path Get Transactions: Because the order management system only passes back the order id, we need to make a call to Elastic Path to retrieve the rest of the required information: the Stripe reference and the Elastic Path transaction id.

  • Stripe Capture PaymentIntent: If the order validation status is verified, we'll run this Component, which uses the Stripe reference id we saved to the Elastic Path transaction to call Stripe to capture the payment.

  • Elastic Path Capture Transaction: If the Stripe capture is a success, this component runs to capture the transaction in Elastic Path and mark the order complete.

Note: This recipe uses Stripe's test tokens and payment methods, and does not include methods for passing PCI data from the storefront to Stripe.

DX Engine Configuration Details

You will need to configure a connection to Stripe and to Elastic Path Commerce Cloud.

Below are instructions on how to configure the two connections, the listener, and each of the Components discussed above.

Create a Connection to Stripe

  • Navigate to the Connections page (Settings --> Connections).
  • Click the Configure Connection button.
  • Enter the following and click Submit:
FieldValue
Connection Codestripe-connection
Connection NameStripe Connection
ConnectorUniversal API Connector
Base URLGet value from: Literal
https://api.stripe.com
Base HeaderHeader: Authorization
Value (JS Expression): 'Bearer ' + secret('stripe-secret-test-key')

Create a Connection to Elastic Path Commerce Cloud

  • Navigate to the Connections page (Settings --> Connections).
  • Click the Configure Connection button.
  • Enter the following and click Submit:
FieldValue
Connection Codeepcc-connection
Connection NameElastic Path Commerce Cloud Connection
ConnectorUniversal API Connector
Base URLGet value from: Literal
https://useast.api.elasticpath.com

Create a Component to checkout as a guest in Elastic Path

  • Navigate to the Experience Components page (Manage Experiences --> Experience Components)
  • Click the Create Experience Component button.
  • Enter the following and click Submit:
FieldValue
Component Codeepcc-checkout-guest
Component NameEPCC Checkout Guest
No RulesChecked
Component TypeConscia - Universal API Connector

The DX Engine will create and prepare the Component for further configuration. You should see the new component appear in the Component listing.

  • Click Edit
  • Enter the following and click Submit
FieldForm TabValue
ConnectionMainElastic Path Commerce Cloud Connection
Webservice PathMainGet value from: JS Expression
/v2/carts/${contextField('cartId')}/checkout
MethodMainPOST
HeadersMainHeader: Authorization
Value (Context Field): implicitToken
HeadersMainHeader: Content-Type
Value (Literal): application/json
BodyMainGet value from: JS Expression
 {
"data": {
"customer": {
"email": "${contextField('customer').email}",
"name": "${contextField('customer').firstName} ${contextField('customer').lastName}"
},
"billing_address": {
"first_name": "${contextField('customer').firstName}",
"last_name": "${contextField('customer').lastName}",
"company_name": "${contextField('address').companyName}",
"line_1": "${contextField('address').addressLine1}",
"line_2": "${contextField('address').addressLine2}",
"city": "${contextField('address').city}",
"county": "${contextField('address').county}",
"region": "${contextField('address').state}",
"postcode": "${contextField('address').postCode}",
"country": "${contextField('address').country}"
},
"shipping_address": {
"first_name": "${contextField('customer').firstName}",
"last_name": "${contextField('customer').lastName}",
"company_name": "${contextField('address').companyName}",
"line_1": "${contextField('address').addressLine1}",
"line_2": "${contextField('address').addressLine2}",
"city": "${contextField('address').city}",
"county": "${contextField('address').county}",
"region": "${contextField('address').state}",
"postcode": "${contextField('address').postCode}",
"country": "${contextField('address').country}"
}
}
}

Create a Component to create a payment intent in Stripe

  • Navigate to the Experience Components page (Manage Experiences --> Experience Components)
  • Click the Create Experience Component button.
  • Enter the following and click Submit:
FieldValue
Component Codestripe-create-payment-intent
Component NameStripe Create PaymentIntent
No RulesChecked
Component TypeConscia - Universal API Connector

The DX Engine will create and prepare the Component for further configuration. You should see the new component appear in the Component listing.

  • Click Edit
  • Enter the following and click Submit
FieldForm TabValue
ConnectionMainStripe Connection
Webservice PathMainGet value from: Literal
/v1/payment_intents
MethodMainPOST
HeadersMainHeader: Content-Type
Value (Literal): application/x-www-form-urlencoded
BodyMainGet value from: JS Expression
'amount=' + componentResponse('epcc-checkout-guest').data.meta.display_price.balance_owing.amount + '&currency=usd&payment_method_types[]=card&payment_method_options[card][capture_method]=manual&confirm=true&payment_method=pm_card_visa&metadata[orderId]=' + componentResponse('epcc-checkout-guest').data.id

Create a Component to initialize a transaction in Elastic Path

  • Navigate to the Experience Components page (Manage Experiences --> Experience Components)
  • Click the Create Experience Component button.
  • Enter the following and click Submit:
FieldValue
Component Codeepcc-initialize-transaction
Component NameEPCC Initialize Transaction
No RulesChecked
Component TypeConscia - Universal API Connector

The DX Engine will create and prepare the Component for further configuration. You should see the new component appear in the Component listing.

  • Click Edit
  • Enter the following and click Submit
FieldForm TabValue
ConnectionMainElastic Path Commerce Cloud Connection
Webservice PathMainGet value from: Literal
/v2/orders/${componentResponse('epcc-checkout-guest').data.id}/payments
MethodMainPOST
HeadersMainHeader: Authorization
Value (Context Field): clientCredentialsToken
HeadersMainHeader: Content-Type
Value (Literal): application/json
BodyMainGet value from: JS Expression
 {
"data": {
"gateway": "manual",
"method": "authorize",
"amount": ${componentResponse('epcc-checkout-guest').data.meta.display_price.balance_owing.amount},
"paymentmethod_meta": {
"custom_reference": "${componentResponse('stripe-create-payment-intent').id}",
"name": "Stripe Payment Intents"
}
}
}

Create a Listener to listen for the order validated webhook

To create a listener you will need to make a direct API call to DX Engine.

POST https://engine-staging.conscia.io/api/listeners

{
"listener": {
"listenerCode": "order-validated",
"name": "Order Validated",
"sync": true,
"endpointRequestSpec": {
"methods": ["POST", "PUT"]
},
"engineRequest": {
"templateCode": "epcc-capture-payment",
"context": {
"epcc_orderId": "`body.orderId`",
"status": "`body.status`"
}
},
"endpointResponse": {
"status": 200,
"body": {
"epcc_trx_response": "`engineResponse.components['epcc-capture-manual-transaction']`"
},
"headers": [
{
"header": "content-type",
"value": "application/json"
}
]
}
}
}

The mocked webhook event looks like this:

POST https://engine-staging.conscia.io/api/experience/_listener/:listenerLocator/:listenerUuid

Body: {"orderId":"75856901-b433-40da-bdbd-c2265769944d", "status":"verified"}

Create a Component to get transactions in Elastic Path

  • Navigate to the Experience Components page (Manage Experiences --> Experience Components)
  • Click the Create Experience Component button.
  • Enter the following and click Submit:
FieldValue
Component Codeepcc-get-transactions-by-orderid
Component NameEPCC Get Transactions by OrderId
No RulesChecked
Component TypeConscia - Universal API Connector

The DX Engine will create and prepare the Component for further configuration. You should see the new component appear in the Component listing.

  • Click Edit
  • Enter the following and click Submit
FieldForm TabValue
ConnectionMainElastic Path Commerce Cloud Connection
Webservice PathMainGet value from: JS Expression
/v2/orders/${contextField('epcc_orderId')}/transactions
MethodMainPOST
HeadersMainHeader: Authorization
Value (Context Field): implicitToken

Create a Component to capture a payment intent in Stripe

  • Navigate to the Experience Components page (Manage Experiences --> Experience Components)
  • Click the Create Experience Component button.
  • Enter the following and click Submit:
FieldValue
Component Codestripe-capture-payment-intent
Component NameStripe Capture PaymentIntent
No RulesChecked
Component TypeConscia - Universal API Connector

The DX Engine will create and prepare the Component for further configuration. You should see the new component appear in the Component listing.

  • Click Edit
  • Enter the following and click Submit
FieldForm TabValue
ConnectionMainStripe Connection
Webservice PathMainGet value from: Literal
/v1/payment_intents/${componentResponse('epcc-get-transactions-by-orderid').data[0].custom_reference}/capture
MethodMainPOST
HeadersMainHeader: Content-Type
Value (Literal): application/x-www-form-urlencoded
Trigger ExpressionConditionscontextField('status') === 'verified'

Create a Component to capture a transaction in Elastic Path

  • Navigate to the Experience Components page (Manage Experiences --> Experience Components)
  • Click the Create Experience Component button.
  • Enter the following and click Submit:
FieldValue
Component Codeepcc-capture-transaction
Component NameEPCC Capture Transaction
No RulesChecked
Component TypeConscia - Universal API Connector

The DX Engine will create and prepare the Component for further configuration. You should see the new component appear in the Component listing.

  • Click Edit
  • Enter the following and click Submit
FieldForm TabValue
ConnectionMainElastic Path Commerce Cloud Connection
Webservice PathMainGet value from: JS Expression
/v2/orders/${contextField('epcc_orderId')}/transactions/${componentResponse('epcc-get-transactions-by-orderid').data[0].id}/capture
MethodMainPOST
HeadersMainHeader: Authorization
Value (Context Field): clientCredentialsToken
HeadersMainHeader: Content-Type
Value (Literal): application/json
Trigger ExpressionConditionscomponentResponse('stripe-capture-payment-intent').status === 'succeeded'

References