Accept a Payment
This guide walks through the complete flow: creating a PaymentIntent on your server, passing it to your frontend, and confirming it to collect payment.
How it works
Customer clicks Pay
→ Your server creates a PaymentIntent
→ Returns client_secret to the browser
→ Meridian.js confirms the payment using the secret
→ Meridian notifies your server via webhook
→ You fulfill the order
Step 1 — Create a PaymentIntent on your server
Always create PaymentIntents server-side — never from the browser.
// server.js
app.post('/create-payment-intent', async (req, res) => {
const { amount, currency } = req.body;
const paymentIntent = await meridian.paymentIntents.create({
amount, // in cents, e.g. 2000 = $20.00
currency, // 'usd'
automatic_payment_methods: { enabled: true },
});
res.json({ clientSecret: paymentIntent.client_secret });
});
danger
Never send the full PaymentIntent object to the browser — only the client_secret. The full object contains sensitive server-side data.
Step 2 — Set up Meridian.js on your frontend
<script src="https://js.meridian.dev/v1/"></script>
<div id="payment-element"></div>
<button id="submit">Pay now</button>
// client.js
const meridian = Meridian('pk_test_yourPublishableKey');
// Fetch the client secret from your server
const { clientSecret } = await fetch('/create-payment-intent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: 2000, currency: 'usd' }),
}).then(r => r.json());
// Mount the Payment Element (handles card, wallet, etc.)
const elements = meridian.elements({ clientSecret });
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
Step 3 — Confirm the payment
document.getElementById('submit').addEventListener('click', async () => {
const { error } = await meridian.confirmPayment({
elements,
confirmParams: {
return_url: 'https://yoursite.com/payment-complete',
},
});
if (error) {
// Show the error to the customer
document.getElementById('error-message').textContent = error.message;
}
// On success, the browser redirects to return_url
});
Step 4 — Fulfill the order via webhook
Don't fulfill the order based on the redirect alone — network issues can prevent the customer from reaching your return_url. Use the payment_intent.succeeded webhook event instead:
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
await fulfillOrder(paymentIntent.metadata.order_id);
break;
Test the flow
Use the test card 4242 4242 4242 4242 with any future expiry and any CVC to simulate a successful payment. See the full list in Test Mode.