Quickstart
Take a test payment end to end: create a customer, save their card, charge it, and refund it. Everything here runs in test mode, so no real money moves.
1. Get your test key
In the dashboard, switch to Test mode and open Developers → API keys. Create a secret key. It starts with sk_test_ and is shown once, so store it somewhere safe, such as your server’s environment:
export COREPAYMENTS_SECRET_KEY=sk_test_... Secret keys go on your server only. Never put one in a browser, an app or a repository.
2. Create a customer
curl https://api.corepayments.app/v1/customers \
-H "Authorization: Bearer $COREPAYMENTS_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Jenny Rosen", "email": "jenny@example.com"}' {
"id": "cus_8Hc1VqT2mXb9Lk4PzR7aQwEy",
"object": "customer",
"livemode": false,
"email": "jenny@example.com",
"name": "Jenny Rosen",
"default_payment_method": null,
"metadata": {},
"created": 1793840114
} 3. Collect card details in the browser
Card numbers never touch your server. corepayments.js shows the card fields on your page and turns what the customer types into a payment method, using your publishable key (pk_test_…, from the same page as your secret key):
<div id="card"></div>
<button id="pay">Pay</button>
<script src="https://js.corepayments.app/v1/corepayments.js"></script>
<script>
const cp = CorePayments('pk_test_...')
const card = cp.fields('card')
card.mount('#card')
document.querySelector('#pay').onclick = async () => {
const { paymentMethod, error } = await cp.createPaymentMethod({
fields: card,
billing: { name: 'Jenny Rosen' },
})
if (error) return alert(error.message)
// Send paymentMethod.id to your server.
}
</script> 4. Save the card to the customer
On your server, attach the payment method to the customer:
curl https://api.corepayments.app/v1/payment_methods/pm_2LmQ.../attach \
-H "Authorization: Bearer $COREPAYMENTS_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"customer": "cus_8Hc1VqT2mXb9Lk4PzR7aQwEy"}' 5. Charge it
Amounts are integers in cents: 2000 is $20.00. Send an Idempotency-Key with every POST so a retry after a network error can’t charge twice; your order ID makes a good one.
curl https://api.corepayments.app/v1/charges \
-H "Authorization: Bearer $COREPAYMENTS_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order_1042" \
-d '{
"amount": 2000,
"currency": "usd",
"customer": "cus_8Hc1VqT2mXb9Lk4PzR7aQwEy",
"payment_method": "pm_2LmQ...",
"description": "Order 1042"
}' A successful charge comes back with status 201 and "status": "succeeded":
{
"id": "ch_3kTq9Xb2VnR8sLp1MwQ4zYcD",
"object": "charge",
"livemode": false,
"amount": 2000,
"amount_captured": 2000,
"amount_refunded": 0,
"currency": "usd",
"status": "succeeded",
"captured": true,
"customer": "cus_8Hc1VqT2mXb9Lk4PzR7aQwEy",
"payment_method": "pm_2LmQ...",
"payment_method_details": {
"type": "card",
"card": {
"brand": "visa",
"last4": "1111",
"exp_month": 12,
"exp_year": 2030,
"funding": "credit"
}
},
"outcome": { "type": "authorized", "decline_code": null, "network_message": null },
"description": "Order 1042",
"metadata": {},
"created": 1793840126
} When it doesn’t succeed
| Response | What happened | What to do |
|---|---|---|
402 card_declined | The card was declined. error.decline_code says why, such as insufficient_funds. | Show error.message to the customer and ask for another card. |
502 processor_unavailable | The payment network didn’t answer in time. The charge exists with status unknown. | Don’t retry with a new key. The charge resolves on its own, with a charge.succeeded or charge.failed event. |
400 | A field is missing or invalid. error.param names it. | Fix the request. |
Test mode has amounts that trigger each of these, so you can try your handling before going live. Every error is listed on the errors page.
6. Refund it
Leave out amount to refund everything, or pass a smaller amount for a partial refund:
curl https://api.corepayments.app/v1/refunds \
-H "Authorization: Bearer $COREPAYMENTS_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund_order_1042" \
-d '{"charge": "ch_3kTq9Xb2VnR8sLp1MwQ4zYcD", "reason": "requested_by_customer"}' Next steps
- Set up webhooks so your system hears about payments, refunds and changes as they happen.
- Read the API basics: pagination, metadata, idempotency and versions.
- Browse every endpoint in the API reference.
- To take real payments, your account needs an approved application. Until then, live charges return
charges_not_enabled. When it’s approved, swap in yoursk_live_key; nothing else changes.