# Make Payments

Source: https://fragment.dev/guides/make-payments

[Documentation index](https://fragment.dev/llms.txt)

<a id="make-payments"></a>

Fragment enables you to make payments that automatically reconcile to your Ledger.

[Visual: PaymentDiagram. View on the HTML page.](https://fragment.dev/guides/make-payments)

<a id="payment-type"></a>

## Payment Type

A Payment Type is a reusable definition of one type of money movement in your Ledger. Define them in your Schema.

```json expandable=true title="Payment Type definition"
{
  "type": "card_payin",
  "typeVersion": 1,
  "status": "active",
  "payment": {
    "amount": "{{order_amount}}",
    "direction": "payin"
  },
  "accounting": {
    "settled": {
      "description": "Order {{order_id}} settled",
      "lines": [
        {
          "key": "settlement",
          "system": "payment_settlement_line",
          "account": {
            "path": "assets-root/payments"
          },
          "amount": "{{settled_amount}}"
        },
        {
          "key": "fee",
          "system": "payment_fee_line",
          "account": {
            "path": "assets-root/payments"
          },
          "amount": "-{{fragment_fee_amount}}"
        },
        {
          "key": "receivable",
          "account": {
            "path": "assets-root/receivable:{{order_id}}"
          },
          "amount": "-{{settled_amount}}"
        },
        {
          "key": "fee-expense",
          "account": {
            "path": "expense-root/payment-fees"
          },
          "amount": "{{fragment_fee_amount}}"
        }
      ]
    }
  }
}
```

<a id="embed-checkout"></a>

## Embed Checkout

Embed Fragment's checkout in your product to begin processing payments.

[Visual: EmbedCheckoutSequenceDiagram. View on the HTML page.](https://fragment.dev/guides/make-payments)

<a id="embed-checkout-create-a-payment"></a>

### Create a Payment

Call `createPayment` from your backend with a Payment Type. Pass the returned `Payment` to your frontend:

```typescript title="backend.ts"
const { createPayment } = await fragment.createPayment({
  ik: orderId,
  ledgerIk: 'main',
  type: 'card_payin',
  typeVersion: 1,
  parameters: {
    amount: '4999',
  },
});

if (createPayment.__typename !== 'CreatePaymentResult') {
  throw new Error(createPayment.message);
}

return { payment: createPayment.payment };
```
```python title="backend.py"
response = await client.create_payment(
    ik=order_id,
    ledger_ik="main",
    type_="card_payin",
    type_version=1,
    parameters={"amount": "4999"},
)

result = response.create_payment
if result.typename__ != "CreatePaymentResult":
    raise Exception(result.message)

return {"payment": result.payment}
```
```go title="backend.go"
parameters := json.RawMessage(`{"amount": "4999"}`)

response, err := queries.CreatePayment(
  context.Background(),
  graphqlClient,
  orderID,
  "main",
  "card_payin",
  1, // typeVersion
  &parameters,
  nil, // paymentMethod
)
if err != nil {
  return nil, err
}

return response.GetCreatePayment(), nil
```
```ruby title="backend.rb"
response = fragment.create_payment({
  ik: order_id,
  ledgerIk: 'main',
  type: 'card_payin',
  typeVersion: 1,
  parameters: { amount: '4999' }
})

result = response.data.create_payment
raise result.message unless result.__typename == 'CreatePaymentResult'

{ payment: result.payment }
```

The Payment starts in `needs_payment_method`. No money moves until the customer confirms the card.

<a id="embed-checkout-render-the-checkout-form"></a>

### Render the checkout form

Use Fragment's [frontend SDK](https://fragment.dev/markdown/api-reference/ledger-sdks.md#frontend) to render a checkout form.

[Visual: checkoutHero. View on the HTML page.](https://fragment.dev/guides/make-payments)

Pass the `Payment` and the customer's email in `billingDetails.email` as props to the component. Mount `Checkout` within `PaymentElements`:

```typescript title="CheckoutPage.tsx"
import {
  Checkout,
  PaymentElements,
  type Payment,
} from '@fragment-dev/payment-elements';
import { PayButton } from './PayButton';

const CheckoutPage = ({
  payment,
  email,
  onSuccess,
}: {
  payment: Payment;
  email: string;
  onSuccess: () => void;
}) => (
  <PaymentElements>
    <Checkout
      payment={payment}
      billingDetails={{ email }}
      onError={(message) => console.error('Payment failed:', message)}
      onConfirmed={onSuccess}
    >
      <PayButton />
    </Checkout>
  </PaymentElements>
);
```

You control the Pay button. Call `confirm()` to charge the card when `canConfirm` is `true`:

```typescript title="PayButton.tsx"
import { usePayment } from '@fragment-dev/payment-elements';

export const PayButton = () => {
  const { confirm, canConfirm, isConfirming, isLoading, error } = usePayment();
  return (
    <>
      <button
        type="button"
        disabled={isLoading || !canConfirm || isConfirming}
        onClick={() => canConfirm && confirm()}
      >
        Pay
      </button>
      {error && <p role="alert">{error}</p>}
    </>
  );
};
```

The SDK calls `onConfirmed` after it charges the card. On a failure, it calls `onError`.

<a id="manage-saved-cards"></a>

## Manage saved cards

Save a user's cards during checkout or onboarding to initiate payments from your backend.

<a id="manage-saved-cards-save-a-card-at-checkout"></a>

### Save a card at checkout

Enable `savePaymentMethod` on the `Checkout` component to save a user's card when the payment confirms:

```typescript title="CheckoutPage.tsx"
const CheckoutPage = ({ payment, email }: { payment: Payment; email: string }) => {
  const [saveCard, setSaveCard] = useState(false);
  return (
    <Checkout
      payment={payment}
      billingDetails={{ email }}
      savePaymentMethod={saveCard}
      onError={logError}
      onConfirmed={showReceipt}
    >
      <SaveCardCheckbox checked={saveCard} onChange={setSaveCard} />
      <PayButton />
    </Checkout>
  );
};
```

Render a checkbox for the payer to choose during checkout:

[Visual: checkoutCardOnFile. View on the HTML page.](https://fragment.dev/guides/make-payments)

Saved cards are tied to the user's email provided in `billingDetails.email`.

<a id="manage-saved-cards-save-a-card"></a>

### Save a card

Save a user's card during onboarding to charge them later.

[Visual: SavePaymentMethodSequenceDiagram. View on the HTML page.](https://fragment.dev/guides/make-payments)

Call `createPaymentMethodSetup` from your backend. Pass the returned `PaymentMethodSetup` to your frontend:

```typescript title="backend.ts"
const { createPaymentMethodSetup } = await fragment.createPaymentMethodSetup({
  ik: requestId,
  ledgerIk: 'main',
  email,
});

if (createPaymentMethodSetup.__typename !== 'CreatePaymentMethodSetupResult') {
  throw new Error(createPaymentMethodSetup.message);
}

return { paymentMethodSetup: createPaymentMethodSetup.paymentMethodSetup };
```
```python title="backend.py"
response = await client.create_payment_method_setup(
    ik=request_id,
    ledger_ik="main",
    email=email,
)

result = response.create_payment_method_setup
if result.typename__ != "CreatePaymentMethodSetupResult":
    raise Exception(result.message)

return {"payment_method_setup": result.payment_method_setup}
```
```go title="backend.go"
response, err := queries.CreatePaymentMethodSetup(
  context.Background(),
  graphqlClient,
  requestID,
  "main",
  email,
)
if err != nil {
  return nil, err
}

return response.GetCreatePaymentMethodSetup(), nil
```
```ruby title="backend.rb"
response = fragment.create_payment_method_setup({
  ik: request_id,
  ledgerIk: 'main',
  email: email
})

result = response.data.create_payment_method_setup
raise result.message unless result.__typename == 'CreatePaymentMethodSetupResult'

{ paymentMethodSetup: result.payment_method_setup }
```

Render `SavePaymentMethod` from Fragment's [frontend SDK](https://fragment.dev/markdown/api-reference/ledger-sdks.md#frontend) inside `PaymentElements` to show the card form:

[Visual: savePaymentMethodHero. View on the HTML page.](https://fragment.dev/guides/make-payments)

Pass the `PaymentMethodSetup` to `SavePaymentMethod`. You control the Save button. Call `submit()` from `usePaymentMethodSetup()` when `canSubmit` is `true`:

```typescript expandable=true title="SaveCardPage.tsx"
import {
  PaymentElements,
  SavePaymentMethod,
  usePaymentMethodSetup,
  type PaymentMethodSetup,
} from '@fragment-dev/payment-elements';

const SaveButton = () => {
  const { submit, canSubmit, state, error } = usePaymentMethodSetup();
  return (
    <>
      <button type="button" disabled={!canSubmit} onClick={() => void submit()}>
        {state.status === 'processing' ? 'Saving…' : 'Save card'}
      </button>
      {state.status === 'declined' && <p role="alert">{state.message}</p>}
      {error && <p role="alert">{error.message}</p>}
    </>
  );
};

export const SaveCardPage = ({ setup }: { setup: PaymentMethodSetup }) => (
  <PaymentElements>
    <SavePaymentMethod
      paymentMethodSetup={setup}
      onPaymentMethodSaved={showConfirmation}
      onPaymentMethodDeclined={showDecline}
      onError={logError}
    >
      <SaveButton />
    </SavePaymentMethod>
  </PaymentElements>
);
```

The SDK calls `onPaymentMethodSaved` after it saves the card, and `onPaymentMethodDeclined` when the card is declined. Any other failure calls `onError`.

<a id="manage-saved-cards-charge-a-saved-card"></a>

### Charge a saved card

Call `createPayment` with the saved card's `id` in `paymentMethod` to make a payment from your backend:

```typescript title="backend.ts"
const { createPayment } = await fragment.createPayment({
  ik: renewalId,
  ledgerIk: 'main',
  type: 'card_payin',
  typeVersion: 1,
  parameters: {
    amount: '4999',
  },
  paymentMethod: { id: savedCardId },
});
```
```python title="backend.py"
response = await client.create_payment(
    ik=renewal_id,
    ledger_ik="main",
    type_="card_payin",
    type_version=1,
    parameters={"amount": "4999"},
    payment_method={"id": saved_card_id},
)
```
```go title="backend.go"
parameters := json.RawMessage(`{"amount": "4999"}`)

response, err := queries.CreatePayment(
  context.Background(),
  graphqlClient,
  renewalID,
  "main",
  "card_payin",
  1, // typeVersion
  &parameters,
  &queries.PaymentMethodMatchInput{Id: savedCardID},
)
```
```ruby title="backend.rb"
response = fragment.create_payment({
  ik: renewal_id,
  ledgerIk: 'main',
  type: 'card_payin',
  typeVersion: 1,
  parameters: { amount: '4999' },
  paymentMethod: { id: saved_card_id }
})
```

The Payment starts in `processing` and follows the same lifecycle as a checkout Payment.

<a id="manage-saved-cards-delete-a-saved-card"></a>

### Delete a saved card

Call `deletePaymentMethod` with the saved card's `id`. After the call, the card can no longer be charged:

```typescript title="backend.ts"
await fragment.deletePaymentMethod({
  id: savedCardId,
});
```
```python title="backend.py"
await client.delete_payment_method(
    id=saved_card_id,
)
```
```go title="backend.go"
queries.DeletePaymentMethod(
  context.Background(),
  graphqlClient,
  savedCardID,
)
```
```ruby title="backend.rb"
fragment.delete_payment_method({
  id: saved_card_id
})
```

<a id="refund-a-payment"></a>

## Refund a Payment

To refund a `settled` Payment, call `refundPayment` with the Payment's `ik`:

```typescript
const { payment, refund } = await fragment.refundPayment({
  ik: refundRequestIk,
  paymentId: settledPayment.ik,
});

console.log(refund.status); // pending
console.log(refund.amount); // 4999
```

Pass `amount` to refund part of the Payment, up to its captured amount:

```typescript
const { payment, refund } = await fragment.refundPayment({
  ik: refundRequestId,
  paymentId: settledPayment.id,
  amount: '2500',
});
```

The funds will immediately move out of your Payment Account and the refund will move to a `pending` status. Refunds will move to the `succeeded` status after a few days. In the event of a failure, the refund will be marked as `failed`.

Fragment keeps its processing fee from the original Payment when you refund it.

<a id="testing"></a>

## Testing

Use the following cards in `sandbox` mode for your testing:

| Card number           | Outcome                                                          |
| :-------------------- | :--------------------------------------------------------------- |
| `4242 4242 4242 4242` | The payment succeeds. A saved card's later charges also succeed. |
| `4000 0000 0000 0002` | The payment is declined.                                         |
| `4000 0000 0000 0341` | The card saves, but a later charge against it is declined.       |
| `5385 3083 6013 5181` | 3D Secure is required. Enter `Checkout1!` on the 3DS screen.     |

The expiry date must be in the future, and use any three-digit CVC.
