# Sell prepaid credit (/docs/tutorials/sell-prepaid-credit)

In this tutorial a customer buys a 20 USD credit pack from your app. Your server charges it to the card they already use for their subscription. When the payment goes through, a webhook adds 20 USD of credit to their account in Comecero. When usage is billed, the credit pays first and only the rest is charged.

## Before you start [#before-you-start]

- An agreement with a metered entitlement, for example `agr_team_42` from [Bill for API calls](/docs/tutorials/bill-for-api-calls).
- A test secret token in `COMECERO_SECRET_TOKEN`, used only on your server.
- A webhook endpoint that checks signatures, as in the [webhooks guide](/docs/guides/webhooks#check-the-signature).

<Steps>

<Step>

## Give the customer a credit balance [#give-the-customer-a-credit-balance]

Credit lives in a `credit` entitlement of the agreement. It has no metric and no price of its own.

```bash title="Request"
curl https://api.comecero.com/api/v1/usage/entitlements \
  -H "Authorization: Bearer $COMECERO_SECRET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entitlement_id": "credit_team_42",
    "kind": "credit",
    "billing_agreement_id": "agr_team_42"
  }'
```

Then point the metered entitlement at it with `funding_source_entitlement_id`. Posting an entitlement with the same id updates it:

```bash title="Request"
curl https://api.comecero.com/api/v1/usage/entitlements \
  -H "Authorization: Bearer $COMECERO_SECRET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entitlement_id": "ent_team_42_api_calls",
    "billing_agreement_id": "agr_team_42",
    "metric_id": "met_api_calls",
    "rate_plan_id": "api_calls_standard",
    "rate_plan_version": 1,
    "funding_source_entitlement_id": "credit_team_42"
  }'
```

Credit is in the agreement's currency. A balance starts at 0.

</Step>

<Step>

## Charge the credit pack [#charge-the-credit-pack]

When the customer clicks **Buy 20 USD of credit**, your server makes an invoice for the pack and pays it with the customer's saved payment method. `meta` says which credit to add, so the webhook knows what to do.

```js title="buy-credit.js"
const API = 'https://api.comecero.com/api/v1';
const headers = { Authorization: `Bearer ${process.env.COMECERO_SECRET_TOKEN}`, 'Content-Type': 'application/json' };

async function call(method, path, body) {
  const response = await fetch(API + path, { method, headers, body: body && JSON.stringify(body) });
  const answer = await response.json();
  if (!response.ok) throw new Error(`${method} ${path}: ${response.status} ${answer.error?.message ?? ''}`);
  return answer;
}

export async function buyCredit({ comeceroCustomerId, creditEntitlementId, amount }) {
  const invoice = await call('POST', '/invoices', {
    customer_id: comeceroCustomerId,
    currency: 'USD',
    draft: false,
    suppress_customer_notification: true,
    items: [{ name: `Prepaid credit, ${amount} USD`, type: 'service', quantity: 1, reference_price: amount }],
    meta: { credit_entitlement_id: creditEntitlementId, credit_amount: amount },
  });

  const methods = await call('GET', `/customers/${encodeURIComponent(comeceroCustomerId)}/payment_methods`);
  const method = methods.data.find((m) => m.is_default) ?? methods.data[0];
  if (!method) throw new Error('no saved payment method: send the customer an invoice link instead');

  return call('POST', `/invoices/${encodeURIComponent(invoice.invoice_id)}/payments`, { payment_method_id: method.payment_method_id });
}
```

`comeceroCustomerId` is the Comecero customer who pays the subscription. It is not your own id. Read it once from [Get a subscription](/api/subscriptions/get-subscription) with `expand=customer`, and store it next to the agreement.

If the payment fails, tell the customer and let them try again. Do not retry it on your own: a retry can charge twice. Without a saved payment method, [create a payment link](/api/invoices/create-invoice-link) and send the customer to it.

</Step>

<Step>

## Add the credit when it is paid [#add-the-credit-when-it-is-paid]

Subscribe to `invoice:paid` (as in the [webhooks guide](/docs/guides/webhooks)). When an invoice with your `meta` is paid, add the credit. Use the invoice id in `idempotency_key`: a webhook sent twice then still adds the credit once.

```js title="webhooks.js"
app.post('/webhooks/comecero', express.raw({ type: 'application/json' }), async (req, res) => {
  if (!isFromComecero(req.body, req.get('x-comecero-signature'))) return res.sendStatus(401);

  const event = JSON.parse(req.body);
  const invoice = event.data;
  if (event.type === 'invoice:paid' && invoice.meta?.credit_entitlement_id) {
    const answer = await fetch(
      `https://api.comecero.com/api/v1/usage/entitlements/${encodeURIComponent(invoice.meta.credit_entitlement_id)}/credits`,
      {
        method: 'POST',
        headers: { Authorization: `Bearer ${process.env.COMECERO_SECRET_TOKEN}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({
          amount: invoice.meta.credit_amount,
          idempotency_key: `invoice_${invoice.invoice_id}`,
          description: `Credit pack, invoice ${invoice.invoice_id}`,
        }),
      },
    );
    if (!answer.ok) return res.sendStatus(500); // Comecero sends the event again later
  }
  res.sendStatus(200);
});
```

The credit amount comes from `meta.credit_amount`, not from the invoice total, so tax on the pack never changes how much credit the customer gets.

The answer says `"added": true` the first time and `"added": false` for a repeat, with the balance either way.

</Step>

<Step>

## Show the balance [#show-the-balance]

```bash title="Request"
curl https://api.comecero.com/api/v1/usage/entitlements/credit_team_42/balance \
  -H "Authorization: Bearer $COMECERO_SECRET_TOKEN"
```

```json title="Response"
{ "object": "entitlement_balance", "entitlement_id": "credit_team_42", "balance": 20, "entries": 1, "billing_region": "us" }
```

In the dashboard, the customer's page under **Usage Billing**, **Customers** shows **Prepaid credit**, with **Add credit** for a credit you give by hand, for example after an outage.

</Step>

</Steps>

## How the credit is used [#how-the-credit-is-used]

Credit is used when a period is billed, at the subscription's renewal, not as each event arrives. For each metered entitlement, Comecero takes the included allowance first, then pays the price of the rest from the credit, and charges only what is left.

With the API calls price from [Bill for API calls](/docs/tutorials/bill-for-api-calls) and 130,000 calls, the usage is 220.00 USD. The 20 USD credit pays 20.00, and 200.00 USD goes on the renewal invoice. The balance is then 0.

The usage meter keeps counting at a zero balance. If your plan should stop when the credit runs out, check the balance against [usage so far](/docs/tutorials/show-usage-in-your-app) in your app and stop the service yourself.

## Next steps [#next-steps]

- [Credits and burndown](/docs/usage-billing/credits) has the full rules, with several entitlements on one credit.
- [Add prepaid credit](/api/usage-entitlements/add-entitlement-credit) in the API reference.
