Skip to main content

Subscriptions with custom checkout

Collect payment details directly on your site using MONEI Components to activate subscriptions. This gives you full control over the checkout experience.

Before You Begin

Choose Your Flow

There are two ways to activate a subscription with MONEI Components:

Token firstActivate first
How it worksCollect payment details → generate token → activate with tokenActivate → get payment ID → collect details → confirm payment
When to useSimpler flow, fewer server roundtripsWhen you need the payment object before collecting details
Component initaccountId + sessionIdpaymentId

Generate a payment token on the client, then activate the subscription server-side with that token.

1. Create a subscription (Server-side)

Create a Subscription on your server. This step is identical to the prebuilt payment page flow.

POST https://api.monei.com/v1/subscriptions
curl --request POST 'https://api.monei.com/v1/subscriptions' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": 1500,
"currency": "EUR",
"interval": "month",
"intervalCount": 1,
"description": "Pro Plan Monthly",
"customer": {
"name": "John Doe",
"email": "john.doe@example.com"
},
"callbackUrl": "https://example.com/subscriptions/callback",
"paymentCallbackUrl": "https://example.com/payments/callback"
}'

2. Add Component to your page (Client-side)

Include monei.js on your checkout page and mount the CardInput component using your accountId and a unique sessionId.

checkout.html
<head>
<title>Checkout</title>
<script src="https://js.monei.com/v3/monei.js"></script>
</head>
<body>
<div id="card-element">
<!-- MONEI Card Input Component will be inserted here -->
</div>
<button id="subscribe-button">Subscribe</button>
<script src="client.js"></script>
</body>
client.js
// Initialize CardInput with your accountId and a unique sessionId
const cardElement = monei.CardInput({
accountId: 'YOUR_ACCOUNT_ID',
sessionId: 'unique_session_id' // Unique per customer session
});

// Render the Component into the container
cardElement.render('#card-element');
important

Use a different sessionId for each customer session. This ensures the token-generating customer matches the paying customer. You must pass the same sessionId when activating the subscription in step 4.

3. Get the payment token (Client-side)

When the customer clicks the subscribe button, submit the card input to generate a one-time paymentToken.

client.js
document.getElementById('subscribe-button').addEventListener('click', async () => {
// Generate a payment token from the card input
const {token, error} = await cardElement.submit();

if (error) {
// Show the error to the customer
console.error('Error:', error);
return;
}

// Send the token and sessionId to your server
const response = await fetch('/activate-subscription', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
subscriptionId: '{{subscription_id}}',
paymentToken: token,
sessionId: 'unique_session_id'
})
});

const result = await response.json();
// Handle the result (e.g., show success message)
});

4. Activate with token (Server-side)

Activate the subscription with the paymentToken and sessionId received from the client.

POST https://api.monei.com/v1/subscriptions/{id}/activate
curl --request POST 'https://api.monei.com/v1/subscriptions/YOUR_SUBSCRIPTION_ID/activate' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"paymentToken": "token_from_client",
"sessionId": "unique_session_id"
}'

5. Handle webhooks (Server-side)

Webhook handling is the same as the prebuilt payment page flow. MONEI sends payment callbacks to paymentCallbackUrl and subscription status changes to callbackUrl. Always verify the signature and return a 200 status code.


Activate first (alternative)

Activate the subscription first to get a payment ID, then collect payment details and confirm client-side.

1. Create a subscription

Same as step 1 above.

2. Activate the subscription (Server-side)

Activate without a paymentToken — the response is a Payment object with a payment.id.

POST https://api.monei.com/v1/subscriptions/{id}/activate
curl --request POST 'https://api.monei.com/v1/subscriptions/YOUR_SUBSCRIPTION_ID/activate' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"completeUrl": "https://example.com/checkout/complete"
}'
important

Use the payment.id from the response to initialize the CardInput component — do not redirect the customer to nextAction.redirectUrl. That URL is for the prebuilt payment page flow.

3. Mount Component and confirm payment (Client-side)

Initialize the CardInput with the paymentId from the activation response, then confirm the payment.

checkout.html
<head>
<title>Checkout</title>
<script src="https://js.monei.com/v3/monei.js"></script>
</head>
<body>
<div id="card-element"></div>
<button id="subscribe-button">Subscribe</button>
<script src="client.js"></script>
</body>
client.js
// Initialize CardInput with the paymentId from the activation response
const paymentId = '{{payment_id}}'; // Passed from your server
const cardElement = monei.CardInput({paymentId: paymentId});
cardElement.render('#card-element');

document.getElementById('subscribe-button').addEventListener('click', async () => {
const {token, error} = await cardElement.submit();

if (error) {
console.error('Error:', error);
return;
}

// Confirm the payment with the generated token
const result = await monei.confirmPayment({
paymentId: paymentId,
paymentToken: token
});

// Show the result to the customer
// Always rely on webhooks for the definitive payment status
console.log('Payment status:', result.status);
});

4. Handle webhooks

Same as the token first flow — MONEI sends webhooks for both payments and subscription status changes.

Before You Go Live

  • Switch to your live mode API key and Account ID.
  • Ensure your payment methods are enabled in live mode.
  • Test your webhook endpoints handle both payment and subscription callbacks correctly.
Test mode limits

In test mode, subscriptions have the following limits:

  • Maximum 3 active subscriptions per account
  • Subscriptions are auto-canceled after 12 payments
  • Minute and hour billing intervals are available for faster testing