Create subscriptions via checkout
This is for when you want to accept payments and directly create subscriptions in one simple form.
If you want to break it out into two steps where you collect payment method first and then later create a subscription please see this guide:
Setup payment method and create subscriptionsA step-by-step guide for setting up payment methods and creating subscriptions using the OpenPay SDK and OpenPay.js
Step 1: Create a secure checkout tokenCopied!
First, you'll need to create a Checkout Session with mode: 'SUBSCRIPTION'. This will result in checkout session information that contains a secure_token to be used in the next steps.
Checkout Session API Example:
Here’s how to generate a secure checkout session using mode: 'SUBSCRIPTION'
:
response = self.client.checkout.create_checkout_session(
CreateCheckoutSessionRequest(
customer_id='TODO_TARGET_CUSTOMER_ID', # (Optional)
customer_email=None,
line_items=[
CreateCheckoutLineItem(price_id='TODO_DESIRED_PRICE_1', quantity=2),
CreateCheckoutLineItem(price_id='TODO_DESIRED_PRICE_1', quantity=3),
],
currency=CurrencyEnum.USD,
mode=CheckoutMode.SUBSCRIPTION,
return_url=RETURN_URL,
success_url=SUCCESS_URL,
)
)
Once the secure_token
is generated, it’s ready to be used on the frontend to securely collect payment details. You’ll pass this token to the <ElementsForm />
component in your React app to handle the payment setup.
Step 2: Implement the payment formCopied!
Use the <ElementsForm />
component to display a secure form where customers can enter their payment details. You will need to render OpenPay Card Elements as well as required billing information to proceed checkout.
React implementation example:
import {
ElementsForm,
FieldName,
CardElement,
} from '@getopenpay/openpay-js-react';
import { FC, PropsWithChildren, useState } from 'react';
interface InputProps {
type?: string;
placeholder: string;
openPayId?: FieldName;
}
const Input: FC<InputProps> = ({ type, placeholder, openPayId }) => (
<input
type={type ?? 'text'}
placeholder={placeholder}
className="w-full text-sm bg-transparent outline-none py-2"
data-opid={openPayId}
/>
);
const StyledWrapper: FC<PropsWithChildren> = (props) => {
const { children } = props;
return <div className="bg-emerald-50 dark:bg-emerald-800 shadow-md px-4 py-2 rounded-md mb-2">{children}</div>;
};
const HorizontalRule: FC = () => <hr className="border-emerald-200 dark:border-emerald-700" />;
function CheckoutForm() {
const [error, setError] = useState<string | undefined>(undefined);
const [amount, setAmount] = useState<string | undefined>(undefined);
const onLoad = (totalAmountAtoms: number) => {
setAmount(`$${totalAmountAtoms / 100}`);
};
const onCheckoutError = (message: string) => {
setError(message);
};
const onCheckoutSuccess = (invoiceUrls: string[], subscriptionIds: string[], customerId: string) => {
console.log('invoiceUrls', invoiceUrls); // Can be used to redirect or display invoices
console.log('subscriptionIds', subscriptionIds); // Useful for retriving details about the subscription
console.log('customerId', customerId) // Created customer's ID or pre-assigned customer's ID
};
return (
<ElementsForm
checkoutSecureToken={`checkout-secure-token-uuid`}
onChange={() => setError(undefined)}
onLoad={onLoad}
onValidationError={(message) => setError(message)}
onCheckoutSuccess={onCheckoutSuccess}
onCheckoutError={onCheckoutError}
>
{({ submit }) => (
<>
<StyledWrapper>
<div className="flex gap-2 items-center justify-between">
<Input placeholder="Given name" openPayId={FieldName.FIRST_NAME} />
<Input placeholder="Family name" openPayId={FieldName.LAST_NAME} />
</div>
<HorizontalRule />
<Input placeholder="Email" type="email" openPayId={FieldName.EMAIL} />
<HorizontalRule />
<div className="flex gap-2 items-center justify-between">
<Input placeholder="Country" openPayId={FieldName.COUNTRY} />
<Input placeholder="ZIP" openPayId={FieldName.ZIP_CODE} />
</div>
</StyledWrapper>
<StyledWrapper>
<CardElement />
</StyledWrapper>
{error && <small className="font-bold text-xs mt-2 text-red-500">{error}</small>}
<button
onClick={submit}
className="px-4 py-2 mt-2 w-full font-bold rounded-lg bg-emerald-500 text-white hover:bg-emerald-400 active:bg-emerald-600"
>
Pay {amount}
</button>
</>
)}
</ElementsForm>
);
}
Key callbacks and props:
-
checkoutSecureToken
: The secure token generated from the backend to link the session. -
onLoad
: Triggered when the checkout form is successfully loaded. It includestotalAmountAtoms
andcurrency
. -
onValidationError
: Handles any validation errors from the form fields. -
onCheckoutSuccess
: A callback that returns theinvoiceUrls
,subscriptionIds
andcustomerId
after the checkout process is successful. -
onCheckoutError
: Handles any checkout error during the process.
For detailed props and callbacks, please refer to the main guide:
Guide (React)Integrating payment elements
Note if you are testing in staging/sandbox - Set baseUrl
prop to point to our sandbox environment, baseUrl
defaults to our production environment.
Example:
<ElementsForm
baseUrl={isStagingEnv ? "https://cde.openpaystaging.com/": undefined}
>
...
</ElementsForm>
Step 3: Handling a successful checkoutCopied!
Once the customer successfully checkout with their payment method, the onCheckoutSuccess
callback is triggered. The callback returns the invoiceUrls
, subscriptionIds
and customerId
.
-
Invoice urls can be used to display invoices or redirect to invoice page
-
Subscription IDs can be used for further processing with OpenPay SDK
-
Customer ID is the created customer on OpenPay side during checkout process. If you are creating the checkout session for specific customer, it will return that specific customer ID