-
-
Notifications
You must be signed in to change notification settings - Fork 8
API Reference
The plugin library is divided into a web side and an api side. You'd import methods from each side depending on where you need them in your app (RedwoodJS documentation on "sides"):
// web-side
import { useStripeCheckout } from '@redwoodjs-stripe/web';
// api-side
import { createStripeCustomer } from '@redwoodjs-stripe/api';
To use the provider wrap the store components or layout in the <StripeProvider/>
. Put the Provider as high up in your "React DOM tree" as possible.
Example
import { StripeProvider } from '@redwoodjs-stripe/web';
// ...
<StripeProvider
customer={{
search: isAuthenticated ? `email: "${emailFromAuthUser}"` : '',
}}
>
<Storefront />
</StripeProvider>;
User mapping is managed via the customer
object passed to the <StripeProvider/>
.
customer
has the following shape:
type Customer = {
search: string | '';
id: string | '';
};
search
accepts a Search API query that is used to search for a Stripe Customer that matches the authenticated user's search string. In most cases your authentication provider will provide you with an email address that you will be able to use to build a search query string to map to a Stripe Customer. Note that a 'search' can only be used when a user is logged in. If nobody is logged in pass ''
or do not pass a search
parameter to the customer object.
In other cases you can pass get the Stripe Customer ID yourself and add that to the customer object. This is an example from the example-store-stripe repo. The Example Store uses DBAuth for authentication. The Stripe Customer Id is saved under stripeId
in the User table. All the logic is on the backend for security. Some services are available to import, but more on that in the API Side Reference. This could look different depending on your authentication method.
Example
api/src/services/users/users.js
import {
stripeCustomerSearch,
createStripeCustomer,
} from '@redwoodjs-stripe/api'
// ...
export const addStripeId = async ({ id: userId }) => {
let stripeId = ''
const { email } = await getCustomerEmail({ id: userId })
// get customerID from Stripe using email
const customer = await stripeCustomerSearch({
query: `email: \"${email}\"`,
})
if (customer == undefined) {
const newCustomer = await createStripeCustomer({ data: { email: email } })
stripeId = newCustomer.id
} else {
stripeId = customer.id
}
return await updateStripeId({
id: userId,
stripeId: stripeId,
})
}
web/src/layouts/MainLayout/MainLayout.js
import { StripeProvider } from '@redwoodjs-stripe/web'
// ...
const { isAuthenticated } = useAuth()
const customerId = isAuthenticated ? getStripeID() : ''
// ...
return (
<StripeProvider customer={{ id: customerId }}>
// ...
</StripeProvider>
useStripeCheckout()
Hook to redirect to Stripe's hosted Checkout.
Returns an object containing a checkout
function. See the comments in the example below for more information on its usage.
Example
import { useStripeCheckout } from '@redwoodjs-stripe/web';
// ...
const { checkout } = useStripeCheckout();
checkout()
Creates a Stripe Checkout Session and redirects to the Stripe Checkout page.
await checkout({
successUrl:
'http://localhost:8910/stripe-demo?success=true&sessionId={CHECKOUT_SESSION_ID}',
cancelUrl: 'http://localhost:8910/stripe-demo?success=false',
});
session
has the following shape:
type Session = {
mode?: string; // optional if cart items have 'type' as part their objects, else defaults to 'once-off'
cart?: Product[]; // 'cart' is not required when using built-in cart
customer: string; // optional when using user mapping via provider
cancelUrl: string;
successUrl: string;
};
useStripeCart()
Hook to manage the Stripe Cart. Returns an object with the cart api (described below).
Example
import { useStripeCart } from '@redwoodjs-stripe/web';
// ...
const { cart, clearCart, addToCart, removeFromCart, editCartItem } =
useStripeCart();
cart
An array of StripeItem
objects. Each item in the array has the following shape:
type StripeItem = {
id: string;
name?: string;
images?: string[];
description?: string;
price?: number;
type?: string;
};
Each object must contain an id
. This id
corresponds to the StripePrice's id
when using the generated StripeProductsCell
.
If you're checking out with both recurring and once-off items, type
needs to be included in the object.
clearCart()
Removes all items from cart
.
clearCart()
addToCart(product)
Adds a Product
to the cart
.
addToCart({
id: 'price_12345',
// ...
});
removeFromCart(id)
Removes an item from the cart
via the item's id
.
removeFromCart('price_12345');
editCartItem(id, details)
Edits the cart item. For example, to change the quantity
:
editCartItem('price_12345', { quantity: 4 });
details
has the following shape:
type Details = {
quantity?: number;
};
useStripeCustomerPortal()
Hook for Stripe's Customer Portal logic. Requires a customer search query to have been passed to the <StripeProvider/>
. In other words, an authenticated user.
Returns an object containing a redirectToStripeCustomerPortal
function.
import { useStripeCustomerPortal } from '@redwoodjs-stripe/web';
// ...
const { redirectToStripeCustomerPortal } = useStripeCustomerPortal();
redirectToStripeCustomerPortal(portalSession, skipAuth = false)
Creates a Stripe Customer Portal Session and redirects to Stripe's Customer Portal page.
await redirectToStripeCustomerPortal(
{
return_url: 'http://localhost:8910/stripe-demo',
},
true
);
portalSession
has the following shape (Stripe Customer Portal API reference):
type PortalSession = {
configuration?: config;
locale?: string;
on_behalf_of?: string;
return_url: string;
};
Set the second parameter, skipAuth
, to true
when testing the Customer Portal redirect if you have not yet set up user authentication in your app. Once authentication is set up please remove the argument.
createStripeCustomerPortalConfig(portalConfig)
Creates a Customer Portal Configuration object.
```ts
type PortalConfig = {
// TODO
}
<StripeItemsCell params={priceParams, productParams}/>
As part of the setup process, a <StripeProductsCell/>
gets generated inside of your app's components
folder.
This Cell returns a list of StripeItems unique to this plugin. It uses a combination of data from Stripe Prices and Stripe Products. The data is combined as follows:
{
id: stripePrice.id,
name: stripeProduct.name,
description: stripeProduct.description,
images: stripeProduct.images,
price: stripePrice.unit_amount,
type: stripePrice.type,
}
The params
passed to the Cell are the same parameters that would be passed to Stripe's Products and Prices list functions. The following example shows how to get a list of Products from active
Stripe Products that have recurring
Stripe Prices. In other words, this example shows a Cell that returns a list of active subscriptions.
import StripeItemsCell from 'src/components/StripeItemsCell/StripeItemsCell';
// ...
<StripeItemsCell
params={{
productParams: { active: true },
priceParams: { type: 'recurring' },
}}
/>;
priceParams
has the following shape (for more information, look at the Stripe Price list API documentation)
type PriceParams = {
active?: boolean;
currency?: string;
product?: string;
created?: stripeCreated;
ending_before?: string;
limit?: number;
lookup_keys?: string;
recurring?: stripeRecurringPrice;
starting_after?: string;
type?: string;
};
productParams
has the following shape (for more information, look at the Stripe Products list API documentation)
type ProductParams = {
active?: boolean;
created?: stripeCreated;
ending_before?: string;
ids?: string[];
limit?: number;
shippable?: boolean;
starting_after?: string;
url?: string;
};
The Cell has it's own UI for illustrative purpose. You should replace the UI with your own.
As part of the setup process, a /stripeWebhook
function is generated. It uses the plugin handler function to verify and manage webhook events.
handleStripeWebhooks(event, context, webhookObject, verify = false)
This function verifies and manages Stripe webhook events.
import { handleStripeWebhooks } from '@redwoodjs-stripe';
// ...
const { results } = await handleStripeWebhooks(event, context, {
'customer.updated': async (e) => {
updateUser(e);
},
'payment_intent.succeeded': (e) => {
sendCustomerThankYouEmail(e);
},
});
webhookObject
is an object mapping webhook event names to handler functions. Each handler function accepts the relevant event object as input.
webhook signature verification is enabled by default, and can be disabled by providing false
for the verify
parameter.
To use webhooks and Stripe webhook events, we recommend installing the Stripe CLI.
After logging in you'll be given a webhook secret. Make sure it matches the STRIPE_WEBHOOK_SK
key in your .env
file
- Listen for webhook events locally
stripe listen --forward-to localhost:8911/stripeWebhook
If you'd prefer to use snakecase, then you can just change
api/src/functions/stripeWebhook/stripeWebhook.js
toapi/src/functions/stripe_webhook/stripe_webhook.js
and the other files in that folder
- Trigger webhook events locally via the CLI
stripe trigger customer.updated
Services used in the plugin can also be imported into your project to build custom Services for more-complicated use cases.
createStripeCheckoutSession(session)
Creates a Stripe Checkout Session and returns a Session object.
import { createStripeCheckoutSession } from '@redwoodjs-stripe/api';
// ...
const newSession = await createStripeCheckoutSession({
mode: 'payment',
cart: cartItems,
});
session
has the following shape:
type Session = {
customer?: Customer = {};
mode: string;
cart: CartItem[];
successUrl: string = 'http://localhost:8910/stripe-demo?success=true&sessionId={CHECKOUT_SESSION_ID}';
cancelUrl: string = 'http://localhost:8910/stripe-demo?success=false';
};
type Customer = {
id: string;
name?: string;
email?: string;
};
type CartItem = {
id: string;
quantity: number;
};
searchLatestStripeCustomer(string)
Accepts a Search API query and returns the Customer object of the last created Customer that matches the search query.
import { searchLatestStripeCustomer } from '@redwoodjs-stripe/api';
// ...
const customer = await searchLatestStripeCustomer('email: "user@test.com"');
createStripeCustomer(customer)
Accepts an object with data to create a new Stripe customer and returns the newly created Stripe Customer object.
import { createStripeCustomer } from '@redwoodjs-stripe/api';
// ...
const customer = await createStripeCustomer({ email });
Schemas are imported from the plugin. You can view the sdl files here. Although I tried to have the sdls match the Stripe objects, there may be times you need something more. For that reason my sdl files follow a naming convention so that you can add your own custom Stripe sdl files without there being a conflict.
The following Stripe API objects have sdl files:
The plugin comes with a few commands that can be accessed via the @redwoodjs-stripe/cli package.
npx @redwoodjs-stripe/cli@latest <command>
If the plugin in does not have what you need, it does not take away your ability to create your own Stripe API calls. In fact it makes it a little bit easier.
Theplugin sdl already has defined some of the Stripe objects. There's already some services that you can use. You can import the stripe
from the plugin for the api
side. It will already have your Stripe API keys saved do no need to reinvent the wheel.
Example if you needed to get a Stripe invoice. It is not currently supported in the plugin so you would need to create your own Stripe API call.
First set up the api
side.
- Create custom service
api/src/services/invoices.js
import { stripe } from '@redwoodjs-stripe/api'
export const invoice = async ({ id }) => {
const res = await stripe.list.invoices.retrieve(id)
return res
}
- Create custom sdl
api/src/graphql/invoices.sdl.js
export const schema = `
type invoice {
id: ID!
customer: String
description: String
payment_intent: String
}
`
- Create custom query
api/src/graphql/invoices.sdl.js
//...
type Query {
invoice(id: ID!): invoice @requireAuth
}
Note: query name matches service name
All done on the api
side. Now to set up the web
side
This example will use Redwoodjs' Cells to display the data. There are other ways to trigger api calls that are free to use. Cells are just a bit easier because they manage the view (UI) according to the state of your API call. If you used the plugin setup command then you will have an example of a Cell
at web/src/components/StripeItemsCell/StripeItemsCell.js
(you can also find it here)
- Generate a
<Cell/>
.
yarn rw g cell Invoice
- Import the
<InvoiceCell/>
wherever you need the UI to show.web/src/pages/HomePage/HomePage.js
import InvoiceCell from 'src/components/InvoiceCell'
const HomePage = () => {
return (
<div>
<h1>Home</h1>
<InvoiceCell id={'in_1NiCEu2eZvKYlo2CQgw5IdpD'} />
</div>
)
}
export default HomePage
- Define a query for fetching an invoice inside the Cell.
web/src/components/InvoiceCell/InvoiceCell.js
export const QUERY = gql`
query($id: ID!) {
invoice(id: $id) {
description
customer
}
}
`
- Define UI based on status.
web/src/components/InvoiceCell/InvoiceCell.js
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Could not find the invoice you are looking for</div>
export const Failure = ({ error }) => (
<div>Error loading invoice: {error.message}</div>
)
export const Success = ({ invoice }) => {
return(
<article>
<h3>Invoice for customer:{invoice.customer}
<p>{invoice.description}</p>
</article>
)
}
And that's that
Sets up the plugin by copying boilerplate and creating a demo store.
npx @redwoodjs-stripe/cli@latest setup
Installs the latest version of the api
and web
sides of the plugin
npx @redwoodjs-stripe/cli@latest upgrade