> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usepaykit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Chapa

> Chapa provider for PayKit.

```bash theme={null}
npm install @paykit-sdk/chapa
```

## Setup

<Tabs>
  <Tab title="Environment variables">
    ```typescript theme={null}
    import { PayKit } from '@paykit-sdk/core';
    import { chapa } from '@paykit-sdk/chapa';

    export const paykit = new PayKit(chapa());
    ```

    Required env vars:

    ```bash theme={null}
    CHAPA_SECRET_KEY=CHASECK_TEST-...
    ```
  </Tab>

  <Tab title="Direct config">
    ```typescript theme={null}
    import { PayKit } from '@paykit-sdk/core';
    import { createChapa } from '@paykit-sdk/chapa';

    export const paykit = new PayKit(
      createChapa({ secretKey: 'CHASECK_TEST-...', isSandbox: true }),
    );
    ```
  </Tab>
</Tabs>

## How it works

```typescript theme={null}
const checkout = await paykit.checkouts.create({
  customer: { email: 'user@example.com' },
  item_id: 'my-product',
  session_type: 'one_time',
  quantity: 1,
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
  provider_metadata: { amount: '400', currency: 'ETB' },
});

// Redirect the customer to checkout.payment_url

// Once they're back, confirm the payment
const payment = await paykit.payments.retrieve(checkout.id);

// Still pending? You can cancel it
await paykit.payments.cancel(checkout.id);
```

Chapa doesn't have a customer or subscription API — customer details
are captured inline with each transaction instead.

## Webhooks

Chapa signs webhooks using your **secret key** — there's no separate
webhook signing secret to generate or configure.

```typescript theme={null}
const webhook = paykit.webhooks
  .setup({ webhookSecret: process.env.CHAPA_SECRET_KEY! })
  .on('payment.updated', async event => {
    /* charge.success */
  })
  .on('payment.failed', async event => {
    /* charge.failed / charge.cancelled */
  })
  .on('refund.created', async event => {
    /* charge.refunded or charge.reversed */
  })
  .on('invoice.generated', async event => {
    /* charge.success */
  });

await webhook.handle({
  body: rawBody,
  headersAsObject: Object.fromEntries(request.headers),
  fullUrl: request.url,
});
```

Chapa sends two signature headers on every webhook request — the
handler accepts either one:

* `x-chapa-signature`: `HMAC-SHA256(secretKey, JSON.stringify(payload))`
* `chapa-signature`: `HMAC-SHA256(secretKey, secretKey)`

### Raw Chapa events

Opt into any native Chapa event — fully typed against Chapa's payload
shapes:

```typescript theme={null}
paykit.webhooks
  .setup({ webhookSecret: process.env.CHAPA_SECRET_KEY! })
  .on('chapa.charge.success', async event => {
    // event.data is typed as a Chapa transaction payload
  })
  .on('chapa.payout.success', async event => {
    // event.data is typed as a Chapa payout payload
  });
```

Every webhook always emits its raw event first, alongside the
matching standard PayKit event below where one applies. Payouts have
no PayKit equivalent, so they're only available as raw events.

| Raw event                 | PayKit event                            |
| ------------------------- | --------------------------------------- |
| `chapa.charge.success`    | `payment.updated` + `invoice.generated` |
| `chapa.charge.*` (failed) | `payment.failed`                        |
| `chapa.charge.refunded`   | `refund.created`                        |
| `chapa.charge.reversed`   | `refund.created`                        |
| `chapa.payout.success`    | *(raw only)*                            |
| `chapa.payout.*` (failed) | *(raw only)*                            |

## provider\_metadata

`amount` and `currency` are required in `provider_metadata` for
checkout because Chapa does not infer them from the item:

```typescript theme={null}
// checkout.provider_metadata requires amount and currency
await paykit.checkouts.create({
  customer: { email: 'user@example.com' },
  item_id: 'my-product',
  session_type: 'one_time',
  quantity: 1,
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
  provider_metadata: {
    amount: '400', // in the standard currency unit (e.g. Birr), as a string
    currency: 'ETB', // ETB or USD
  },
});

// refund.provider_metadata is typed as { reference?: string }
await paykit.refunds.create({
  payment_id: 'tx_ref_from_the_original_transaction',
  amount: 400,
  reason: 'requested_by_customer',
  provider_metadata: {
    reference: 'REF123', // your own distinctive reference for the refund request
  },
});
```
