Skip to content

docs: Checkout polish #1690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 6, 2024
Merged
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 98 additions & 88 deletions site/docs/pages/checkout/checkout.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import App from '../../components/App';

# `<Checkout />`

The `Checkout` component provides a one-click checkout experience for onchain commerce.
The `Checkout` component provides a one-click checkout experience for onchain commerce - all for free.

Our all-in-one solution simplifies payment processing for onchain developers, removing complex integrations, high fees, and onboarding friction. Whether you're selling digital goods, services, or in-game items, this tool is for you.

Expand All @@ -20,12 +20,15 @@ Our all-in-one solution simplifies payment processing for onchain developers, re
- **Plug-and-Play Integration:** Add our `Checkout` button with just a few lines of code. No backend required.
- **Seamless Onboarding:** Support Passkey wallets to eliminate onboarding drop-offs.
- **Real-time Merchant Tooling:** Get instant payment tracking, analytics, and reporting.
- **Dynamic Payment Flows:** Generate charges on the fly, handle variable pricing, and pass in custom metadata.

## Prerequisites

Before using the `Checkout` component, ensure you've completed the [Getting Started](/getting-started) steps.

To use the `Checkout` component, you'll need to provide an API Key in `OnchainKitProvider`. You can get one following our [Getting Started](/getting-started#get-your-public-api-key) steps.
::::tip
To use the `Checkout` component, you'll need to provide a Client API Key in `OnchainKitProvider`. You can get one following our [Getting Started](/installation/nextjs#get-your-client-api-key) steps.
:::::

### Starting a new project

Expand Down Expand Up @@ -63,18 +66,15 @@ Wrap the `<OnchainKitProvider />` around your app, following the steps in [Getti

## Quickstart

### Option 1: Simple Product Checkout
Ideal for fixed-price items. Get started with minimal setup.

::::steps

### Sign up for a Coinbase Commerce account
<img alt="Create a product"
src="https://onchainkit.xyz/assets/commerce-1.png"
height="364"/>
Head to [Coinbase Commerce](https://beta.commerce.coinbase.com/) and sign up. This is where you’ll manage transactions, view reports, and configure payments.

### Create a product and copy the `productId`
<img alt="Copy productId"
src="https://onchainkit.xyz/assets/commerce-2.png"
height="364"/>
In the Coinbase Commerce dashboard, create a new product and copy the `productId`.

### Import the component
Expand All @@ -89,17 +89,44 @@ import { Checkout, CheckoutButton, CheckoutStatus } from '@coinbase/onchainkit/c
```
::::

### Option 2: Dynamic Charges
For variable pricing, custom metadata, or multi-product checkouts, use backend-generated charges.

::::steps

### Sign up for a Coinbase Commerce account
Head to [Coinbase Commerce](https://beta.commerce.coinbase.com/) and sign up. This is where you’ll manage transactions, view reports, and configure payments.

### Create a Coinbase Commerce API Key
In the [Coinbase Commerce dashboard](https://beta.commerce.coinbase.com/settings/security), create a new API Key under `Security` in `Settings`.

### Set up a backend to create charges dynamically using the Coinbase Commerce API.
See [Using chargeHandler](/checkout/checkout#using-chargehandler) for a code example.

### Pass the chargeID into Checkout via the chargeHandler prop.

```tsx
const chargeHandler = async () => {
const response = await fetch('/createCharge', { method: 'POST' });
const { id } = await response.json();
return id; // Return charge ID
};

<Checkout chargeHandler={chargeHandler}>
<CheckoutButton />
</Checkout>
```
::::

That's it! Starting selling onchain with just a few lines of code.

## Usage

### Configuring a checkout

You can create products on the Coinbase Commerce Portal and use them in the `Checkout` component through the `productId` prop.

If you'd like to create product metadata programmatically or implement a multi-product checkout, please see [Advanced Usage](/checkout/checkout#advanced-usage).
#### Using `productId`

Coinbase Commerce charges a [1% fee](https://help.coinbase.com/en/commerce/getting-started/fees) associated with all payments.
You can create products on the Coinbase Commerce Portal and use them in the `Checkout` component through the `productId` prop.

```tsx twoslash
import { Checkout, CheckoutButton } from '@coinbase/onchainkit/checkout';
Expand All @@ -121,6 +148,63 @@ export default function PayComponents() {
</Checkout>
</App>

#### Using `chargeHandler`

Alternatively, you can create charges dynamically using the Coinbase Commerce API [Create Charge](https://docs.cdp.coinbase.com/commerce-onchain/reference/creates-a-charge) endpoint and pass the chargeID into Checkout via the `chargeHandler` prop.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: pass -> passing

or probably better:

Alternatively, you can create charges dynamically using the Coinbase Commerce API Create Charge endpoint by passing the chargeID into Checkout via the chargeHandler prop.

or

Alternatively, you can create charges dynamically by using the Coinbase Commerce API Create Charge endpoint and passing the chargeID into Checkout via the chargeHandler prop.


This function must have the signature `() => Promise<string>` and must return a valid chargeId created by the create charge endpoint.

:::tip
To create charges, you'll need a Coinbase Commerce [API Key](https://docs.cdp.coinbase.com/commerce-onchain/docs/getting-started).
:::

:::danger[⚠️ Warning]
You should protect your Coinbase Commerce API Key by only creating charges server-side.
:::

:::code-group
```ts [backend.ts]
// This backend endpoint should create a charge and return the response.
app.post('/createCharge', async (req: Request, res: Response) => {
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CC-Api-Key': 'your_api_key_here' // Replace this with your Coinbase Commerce API Key
},
body: JSON.stringify({
local_price: { amount: '1', currency: 'USDC' },
pricing_type: 'fixed_price',
metadata: { some_field: "some_value" } // Optional: Attach metadata like order ID or customer details
}),
};

const response = await fetch('https://api.commerce.coinbase.com/charges', options);
const data = await response.json();

res.json(data);
});

```

```tsx twoslash [frontend.tsx]
import { Checkout, CheckoutButton } from '@coinbase/onchainkit/checkout';

const chargeHandler = async () => {
const response = await fetch('https://your-backend.com/createCharge', { method: 'POST' });
const { id } = await response.json();
return id; // Return charge ID
};

<Checkout chargeHandler={chargeHandler}>
<CheckoutButton />
</Checkout>
```
:::

Note that `productId` and `chargeHandler` are mutually exclusive and only one can be provided as a prop to Checkout.

### Handling a successful checkout

To handle successful checkouts, use the `onStatus` prop to listen for the `success` callback.
Expand Down Expand Up @@ -163,8 +247,8 @@ const statusHandler = async (status: LifecycleStatus) => { // [!code focus]
</Checkout>
// ---cut-after---
```
:::tip[Coinbase Commerce API]
This is an authenticated endpoint. To verify charges, you'll need a Coinbase Commerce [API Key](https://docs.cdp.coinbase.com/commerce-onchain/docs/getting-started).
:::tip
To verify charges, you'll need a Coinbase Commerce [API Key](https://docs.cdp.coinbase.com/commerce-onchain/docs/getting-started).
:::

:::danger[⚠️ Warning]
Expand Down Expand Up @@ -313,80 +397,6 @@ export default function PayComponents() {

## Advanced Usage

### Shopping Carts and Multi-Product Checkout

You can accept payments for arbitrary product metadata using the Coinbase Commerce [create charge](https://docs.cdp.coinbase.com/commerce-onchain/reference/creates-a-charge) endpoint. This is useful if you have an existing inventory management system or want to implement custom features like multi-product checkouts, carts, etc.

:::tip[Coinbase Commerce API]
This is an authenticated endpoint. To create charges, you'll need a Coinbase Commerce [API Key](https://docs.cdp.coinbase.com/commerce-onchain/docs/getting-started).
:::

#### Example server side code

This Typescript example uses [Express](https://expressjs.com/) and [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).

```tsx twoslash [server.ts]
import express, { Request, Response } from 'express';
const fetch = require('node-fetch');

const app = express();
const port = 3000;

app.use(express.json());

// ---cut-before---
// This endpoint should create a charge and return the response.
app.post('/createCharge', async (req: Request, res: Response) => {
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CC-Api-Key': 'your_api_key_here' // Replace this with your Coinbase Commerce API Key
}
};

const response = await fetch('https://api.commerce.coinbase.com/charges', options);
const data = await response.json();

res.json(data);
});
// ---cut-after---
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
```

:::danger[⚠️ Warning]
Charges should only be created server-side. If you create charges on the client, users will be able to create charges associated with your Commerce Merchant account.
:::

We expose a `chargeHandler` prop on the `Checkout` component which takes a callback that is invoked every time the Checkout button is clicked.

This function **must** have the signature `() => Promise<string>` and **must** return a valid `chargeId` created by the create charge endpoint.

Note that `productId` and `chargeHandler` are mutually exclusive and only one can be provided as a prop to `Checkout`.

```tsx twoslash
import { Checkout, CheckoutButton } from '@coinbase/onchainkit/checkout';


// ---cut-before---
const chargeHandler = async () => { // [!code focus]
// Create a charge on your backend server using the Create Charge API // [!code focus]
// Replace this URL with your backend endpoint // [!code focus]
const res = await fetch('api.merchant.com/createCharge'); // [!code focus]
const data = await res.json(); // [!code focus]
return data.id; // Return the chargeId // [!code focus]
} // [!code focus]

<Checkout chargeHandler={chargeHandler}> // [!code focus]
<CheckoutButton />
</Checkout>
// ---cut-after---
```


### Listening to the component lifecycle

You can use our Checkout [`LifecycleStatus`](/checkout/types#lifecyclestatus) and the `onStatus` prop to listen to transaction states.
Expand Down
Loading