Draft. Step 3 uses corepayments.js, which isn't released yet. The other steps work today.

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

ResponseWhat happenedWhat to do
402 card_declinedThe 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_unavailableThe 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.
400A 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 your sk_live_ key; nothing else changes.