Orders & Payments
Integrate payment collection with orders using your own payment provider or managed payment sessions
Orders and payment collection work together to handle purchases in the Connect API. Orders track what a customer is buying, while payments – collected through your own provider or via managed payment sessions – fulfill the payment requirement before the order can be submitted.
Core Concept
Every order has requirements that must be met before submission:
- requiresPayment: Whether the order needs payment before it can be fulfilled
- requiresPaymentProfile: Whether a stored payment method is needed for future billing
- requiresSigning: Whether the order requires a digital signature
Each requirement can be NOT_REQUIRED, OPTIONAL, or REQUIRED. Check these after calculating the price to determine the correct submission flow.
Zero-total orders may not require payment at all. Always check the order requirements rather than assuming payment is needed.
Quick Path
Create Order
Create an order and add line items for the products the customer wants to buy.
Calculate Price
Calculate taxes and totals to determine the amount due.
Check Requirements
Inspect the order requirements to determine if payment, a payment profile, or signing is needed.
Collect Payment
Collect payment through your own provider or use a managed payment session.
Submit Order
Submit the order with the payment reference to begin fulfillment.
Choosing a Payment Approach
The Connect API gives you full flexibility in how you collect payments. You can use your own payment infrastructure or leverage managed payment sessions depending on your needs.
Your Own Provider (Recommended)
Collect payment through your existing payment stack (Stripe, Adyen, Braintree, etc.) and pass the reference when submitting the order. Full control over the checkout experience, payment methods, and provider relationship.
Hosted Payment Page
Use a managed payment session with hosted: true to get a provider-hosted checkout URL.
Redirect customers to a pre-built payment page with no frontend payment UI to build.
Embedded Payment Widget
Use a managed payment session with hosted: false to get provider credentials for rendering a
payment form directly in your UI using the provider’s SDK.
When to Use Each Approach
| Approach | Best for |
|---|---|
| Your own provider | Teams with an existing payment infrastructure who want full control over the payment experience, provider selection, and checkout flow |
| Hosted payment page | Quick integrations where you want a turnkey payment UI without building frontend payment components |
| Embedded widget | Teams who want a managed payment backend but need to customize the payment UI within their application |
Most integrations use the external payment approach because it lets you keep your existing payment provider, maintain a single payment relationship, and have complete control over the customer checkout experience.
1. Create an Order and Add Line Items
Create an order for a customer and include the products they want to purchase.
curl -X POST "https://apiv2.example.com/api/v2/orders" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"customerId": "123e4567-e89b-12d3-a456-426614174000"
},
"lineItems": [
{
"type": "SUBSCRIPTION",
"lineItemId": "sub-1",
"productOfferingId": "offering-id",
"subscriber": {
"name": "Jane Doe",
"email": "jane@example.com"
}
}
]
}'const response = await fetch('https://apiv2.example.com/api/v2/orders', {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer: {
customerId: '123e4567-e89b-12d3-a456-426614174000',
},
lineItems: [
{
type: 'SUBSCRIPTION',
lineItemId: 'sub-1',
productOfferingId: 'offering-id',
subscriber: {
name: 'Jane Doe',
email: 'jane@example.com',
},
},
],
}),
});
const order = await response.json();
const orderId = order.orderId;You can also add line items to an existing order separately:
curl -X POST "https://apiv2.example.com/api/v2/orders/{orderId}/line-items" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "ADDON",
"lineItemId": "addon-1",
"productOfferingId": "addon-offering-id",
"parentLineItemId": "sub-1"
}'await fetch(`https://apiv2.example.com/api/v2/orders/${orderId}/line-items`, {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'ADDON',
lineItemId: 'addon-1',
productOfferingId: 'addon-offering-id',
parentLineItemId: 'sub-1',
}),
});See Create Order and Add Line Items
2. Calculate the Price
Calculate taxes and totals before collecting payment.
curl -X POST "https://apiv2.example.com/api/v2/orders/{orderId}/calculate-price" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json"const priceResponse = await fetch(
`https://apiv2.example.com/api/v2/orders/${orderId}/calculate-price`,
{
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
},
);
const pricedOrder = await priceResponse.json();
console.log('Total:', pricedOrder.pricing.total);
console.log('Currency:', pricedOrder.pricing.currency);3. Check Order Requirements
After calculating the price, inspect the order to determine what is needed before submission.
# Fetch the order and inspect the requirements object
curl "https://apiv2.example.com/api/v2/orders/{orderId}" \
-H "X-API-Key: $CONNECT_API_KEY"const orderResponse = await fetch(`https://apiv2.example.com/api/v2/orders/${orderId}`, {
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
},
});
const orderDetails = await orderResponse.json();
const { requiresPayment, requiresPaymentProfile, requiresSigning } = orderDetails.requirements;
if (requiresPayment === 'REQUIRED') {
// Collect payment through your provider or via a payment session
}
if (requiresPaymentProfile === 'REQUIRED') {
// Set up a payment profile session for recurring billing
}
if (requiresSigning === 'REQUIRED') {
// Create a signing session for digital signature
}See Get Order
4. Collect Payment
Once you know the order requires payment, choose one of the following approaches.
Option A: Your Own Payment Provider (Recommended)
Collect payment through your existing payment provider (Stripe, Adyen, Braintree, or any other provider). This approach gives you full control over the checkout experience and lets you use your existing payment infrastructure without any additional dependencies.
After collecting payment on your side, submit the order with an externalPayment reference:
# Step 1: Collect payment through your own provider
# (This happens in your existing payment flow)
# Step 2: Submit the order with the payment reference
curl -X POST "https://apiv2.example.com/api/v2/orders/{orderId}/submit" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalPayment": {
"reference": "pi_3ABC123def456",
"receiptDescription": "Subscription activation payment",
"receiptUrl": "https://yourapp.com/receipts/abc123"
}
}'// Step 1: Collect payment through your own provider
// (This happens in your existing payment flow)
// Step 2: Submit the order with the payment reference
const submitResponse = await fetch(`https://apiv2.example.com/api/v2/orders/${orderId}/submit`, {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
externalPayment: {
reference: 'pi_3ABC123def456',
receiptDescription: 'Subscription activation payment',
receiptUrl: 'https://yourapp.com/receipts/abc123',
},
}),
});
const submittedOrder = await submitResponse.json();
console.log('Order state:', submittedOrder.state);The externalPayment object accepts:
| Field | Required | Description |
|---|---|---|
reference |
Yes | The payment reference or transaction ID from your provider |
receiptDescription |
No | A human-readable description of the payment |
receiptUrl |
No | A URL to the payment receipt or confirmation page |
When using external payments, you are responsible for collecting the correct amount and handling refunds through your payment provider.
See Submit Order
Option B: Hosted Payment Page
If you prefer a managed payment flow, create a payment session with hosted: true to get a checkout URL. Redirect the customer to the provider-hosted payment page.
curl -X POST "https://apiv2.example.com/api/v2/payment-sessions" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"orderId": "{orderId}",
"paymentProvider": "STRIPE",
"hosted": true,
"returnUrl": "https://yourapp.com/payment/success",
"cancelUrl": "https://yourapp.com/payment/cancel"
}'
# Redirect the customer to provider.checkoutUrl from the responseconst sessionResponse = await fetch('https://apiv2.example.com/api/v2/payment-sessions', {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
orderId: orderId,
paymentProvider: 'STRIPE',
hosted: true,
returnUrl: 'https://yourapp.com/payment/success',
cancelUrl: 'https://yourapp.com/payment/cancel',
}),
});
const session = await sessionResponse.json();
// Redirect the customer to the hosted payment page
window.location.href = session.provider.checkoutUrl;After the customer completes payment, they are redirected to your returnUrl. Poll the payment session to confirm the status before submitting the order.
# Poll the payment session status
curl "https://apiv2.example.com/api/v2/payment-sessions/{paymentSessionId}" \
-H "X-API-Key: $CONNECT_API_KEY"
# When the status is COMPLETED, submit the order with the payment session reference
curl -X POST "https://apiv2.example.com/api/v2/orders/{orderId}/submit" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"paymentSessionId": "{paymentSessionId}"
}'const statusResponse = await fetch(
`https://apiv2.example.com/api/v2/payment-sessions/${session.paymentSessionId}`,
{
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
},
},
);
const sessionStatus = await statusResponse.json();
if (sessionStatus.status === 'COMPLETED') {
// Submit the order with the payment session reference
await fetch(`https://apiv2.example.com/api/v2/orders/${orderId}/submit`, {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
paymentSessionId: session.paymentSessionId,
}),
});
}See Create Payment Session and Get Payment Session
Option C: Embedded Payment Widget
Create a payment session with hosted: false (or omit the field) to get provider credentials for rendering a payment form directly in your UI.
curl -X POST "https://apiv2.example.com/api/v2/payment-sessions" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"orderId": "{orderId}",
"paymentProvider": "STRIPE"
}'
# Use provider.clientSecret and provider.publishableKey from the response
# to render a payment widgetconst sessionResponse = await fetch('https://apiv2.example.com/api/v2/payment-sessions', {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
orderId: orderId,
paymentProvider: 'STRIPE',
}),
});
const session = await sessionResponse.json();
// Use the provider credentials to render a payment widget
const { clientSecret, publishableKey } = session.provider;Use the returned credentials with the provider’s client SDK. For example, with Stripe Elements:
const stripe = Stripe(publishableKey);
const elements = stripe.elements({ clientSecret });
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
// When the customer submits the form:
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: 'https://yourapp.com/payment/success',
},
});
After the payment completes, submit the order with the payment session ID:
curl -X POST "https://apiv2.example.com/api/v2/orders/{orderId}/submit" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"paymentSessionId": "{paymentSessionId}"
}'await fetch(`https://apiv2.example.com/api/v2/orders/${orderId}/submit`, {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
paymentSessionId: session.paymentSessionId,
}),
});Zero-Total Orders with Payment Profile
For orders with a zero total that still require a stored payment method (e.g., trial subscriptions), submit with a payment profile session ID instead.
curl -X POST "https://apiv2.example.com/api/v2/orders/{orderId}/submit" \
-H "X-API-Key: $CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"paymentProfileSessionId": "profile-session-id"
}'const submitResponse = await fetch(`https://apiv2.example.com/api/v2/orders/${orderId}/submit`, {
method: 'POST',
headers: {
'X-API-Key': process.env.CONNECT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
paymentProfileSessionId: 'profile-session-id',
}),
});Order States
| State | Description |
|---|---|
PENDING |
Order is in cart state, can be modified |
PENDING_PAYMENT |
Order is locked and awaiting payment completion |
SUBMITTED |
Order has been submitted for processing |
PENDING_APPROVAL |
Order is pending approval |
PROCESSING |
Order is being fulfilled |
COMPLETED |
Order has been successfully fulfilled |
CANCELLED |
Order was canceled before completion |
EXPIRED |
Order expired due to inactivity |
FAILED |
Order fulfillment failed |
Payment Session Statuses
| Status | Description |
|---|---|
PENDING |
Session created, awaiting customer payment |
REQUIRES_ACTION |
Additional action needed from the customer (e.g., 3D Secure) |
COMPLETED |
Payment successfully collected |
FAILED |
Payment failed |
Requirements Reference
When you retrieve an order after calculating the price, the requirements object tells you what is needed before submission.
| Requirement | Values | Description |
|---|---|---|
requiresPayment |
NOT_REQUIRED, OPTIONAL, REQUIRED |
Whether payment must be collected |
requiresPaymentProfile |
NOT_REQUIRED, OPTIONAL, REQUIRED |
Whether a stored payment method is needed |
requiresSigning |
NOT_REQUIRED, OPTIONAL, REQUIRED |
Whether a digital signature is needed |