448 lines
10 KiB
Plaintext
448 lines
10 KiB
Plaintext
---
|
|
title: Monetary credits
|
|
description: Grant your users a currency-based balance of credits, that various features can draw from
|
|
---
|
|
|
|
When you have multiple features that cost different amounts, you can use a credit system to deduct usage from a single balance. This can be great to simplify billing and usage tracking, especially when you have lots of features.
|
|
|
|
## Example case
|
|
|
|
We have a AI chatbot product with 2 different models, and each model costs a different amount to use.
|
|
|
|
- Basic message: $1 per 100 messages
|
|
- Premium message: $10 per 100 messages
|
|
|
|
And we have the following plans:
|
|
|
|
- Free tier: $5 credits per month for free
|
|
- Pro tier: \$10 credits per month, at \$10 per month
|
|
|
|
Users should also be able to top up their balance with more credits.
|
|
|
|
## Configure Pricing
|
|
|
|
<Steps>
|
|
<Step>
|
|
#### Create Features
|
|
|
|
Create a `metered` `consumable` feature for each message type, so that we can track the usage of each:
|
|
|
|
<Frame>
|
|
<img src="/assets/guides/monetary-credits/features-light.png" className="block dark:hidden" />
|
|
<img src="/assets/guides/monetary-credits/features-dark.png" className="hidden dark:block" />
|
|
</Frame>
|
|
</Step>
|
|
|
|
<Step>
|
|
#### Create Credit System
|
|
|
|
Now, we'll create a credit system, where we'll define the cost of each message type. We'll define the cost per message in USD:
|
|
|
|
| Feature | Cost per message (USD) | Credit cost per message (USD)|
|
|
|-----------------------|--------------------------------|----------------------------|
|
|
| Basic message | $1 per 100 messages | 0.01 |
|
|
| Premium message | $10 per 100 messages | 0.10 |
|
|
|
|
<Frame style={{ width: "400px" }}>
|
|
<img src="/assets/guides/monetary-credits/credit-system-light.png" className="block dark:hidden" />
|
|
<img src="/assets/guides/monetary-credits/credit-system-dark.png" className="hidden dark:block" />
|
|
</Frame>
|
|
</Step>
|
|
|
|
<Step>
|
|
#### Create Free, Pro and Top-up Plans
|
|
|
|
Let's create our free and pro plans, and add the credits amounts to each.
|
|
|
|
<Tip>
|
|
Make sure to set the `auto-enable` flag on the free plan, so that it is automatically assigned to new customers.
|
|
</Tip>
|
|
<Frame style={{ width: "400px" }}>
|
|
<img src="/assets/guides/monetary-credits/free-light.png" className="block dark:hidden" />
|
|
<img src="/assets/guides/monetary-credits/free-dark.png" className="hidden dark:block" />
|
|
</Frame>
|
|
<br />
|
|
<Frame style={{ width: "400px" }}>
|
|
<img src="/assets/guides/monetary-credits/pro-light.png" className="block dark:hidden" />
|
|
<img src="/assets/guides/monetary-credits/pro-dark.png" className="hidden dark:block" />
|
|
</Frame>
|
|
|
|
Then, we'll create our top-up plan. We'll add a price to our credit feature, where each credit is worth $1. These top up credits will be `one-off` `prepaid` purchases that never expire.
|
|
|
|
<Frame style={{ width: "500px" }}>
|
|
<img src="/assets/guides/monetary-credits/top-up-light.png" className="block dark:hidden" />
|
|
<img src="/assets/guides/monetary-credits/top-up-dark.png" className="hidden dark:block" />
|
|
</Frame>
|
|
|
|
</Step>
|
|
</Steps>
|
|
|
|
|
|
|
|
## Implementation
|
|
|
|
<Steps>
|
|
<Step>
|
|
#### Create an Autumn Customer
|
|
|
|
When your user signs up, create an Autumn customer. This will automatically assign them the Free plan, and grant them $5 credits per month.
|
|
|
|
<CodeGroup>
|
|
|
|
```jsx React
|
|
import { useCustomer } from "autumn-js/react";
|
|
|
|
const App = () => {
|
|
const { data: customer } = useCustomer();
|
|
|
|
console.log("Autumn customer:", customer);
|
|
|
|
return <h1>Welcome, {customer?.name || "user"}!</h1>;
|
|
};
|
|
```
|
|
|
|
```typescript TypeScript
|
|
import { Autumn } from "autumn-js";
|
|
|
|
const autumn = new Autumn({ secretKey: "am_sk_test_1234" });
|
|
|
|
const customer = await autumn.customers.getOrCreate({
|
|
customerId: "user_123",
|
|
name: "John Yeo",
|
|
email: "john@example.com",
|
|
});
|
|
```
|
|
|
|
```python Python
|
|
from autumn_sdk import Autumn
|
|
|
|
autumn = Autumn("am_sk_test_1234")
|
|
|
|
customer = await autumn.customers.get_or_create(
|
|
customer_id="user_123",
|
|
name="John Yeo",
|
|
email="john@example.com",
|
|
)
|
|
```
|
|
|
|
```bash cURL
|
|
curl -X POST "https://api.useautumn.com/v1/customers" \
|
|
-H "Authorization: Bearer am_sk_test_1234" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"customer_id": "user_123",
|
|
"name": "John Yeo",
|
|
"email": "john@example.com"
|
|
}'
|
|
```
|
|
|
|
</CodeGroup>
|
|
</Step>
|
|
|
|
<Step>
|
|
#### Checking for access
|
|
|
|
Every time our user sends a message to the chatbot, we'll first check if they have enough credits remaining to send the message.
|
|
|
|
The `requiredBalance` parameter will convert the number of messages to credits. Eg, if you pass `requiredBalance: 5` for basic messages, then check will return `allowed: true` if the user has at least 0.05 USD credits remaining.
|
|
|
|
<Note>
|
|
Note how we're interacting with the underlying features (`basic_messages`,
|
|
`premium_messages`) here--not the credit system.
|
|
</Note>
|
|
|
|
<CodeGroup>
|
|
|
|
```jsx React
|
|
import { useCustomer } from "autumn-js/react";
|
|
|
|
export function CheckBasicMessage() {
|
|
const { check, refetch } = useCustomer();
|
|
|
|
const handleCheckAccess = async () => {
|
|
const { allowed } = check({
|
|
featureId: "basic_messages",
|
|
requiredBalance: 1
|
|
});
|
|
|
|
if (!allowed) {
|
|
alert("You've run out of basic message credits");
|
|
} else {
|
|
// proceed with sending message
|
|
await refetch();
|
|
}
|
|
};
|
|
}
|
|
```
|
|
|
|
```typescript TypeScript
|
|
import { Autumn } from "autumn-js";
|
|
|
|
const autumn = new Autumn({ secretKey: "am_sk_test_1234" });
|
|
|
|
const response = await autumn.customers.check({
|
|
customerId: "user_123",
|
|
featureId: "basic_messages",
|
|
requiredBalance: 1,
|
|
});
|
|
|
|
if (!response.allowed) {
|
|
console.log("User has run out of basic message credits");
|
|
return;
|
|
}
|
|
```
|
|
|
|
```python Python
|
|
from autumn_sdk import Autumn
|
|
|
|
autumn = Autumn("am_sk_test_1234")
|
|
|
|
response = await autumn.customers.check(
|
|
customer_id="user_123",
|
|
feature_id="basic_messages",
|
|
required_balance=1,
|
|
)
|
|
|
|
if not response.allowed:
|
|
print("User has run out of basic message credits")
|
|
```
|
|
|
|
```bash cURL
|
|
curl -X POST "https://api.useautumn.com/v1/check" \
|
|
-H "Authorization: Bearer am_sk_test_1234" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"customer_id": "user_123",
|
|
"feature_id": "basic_messages",
|
|
"required_balance": 1
|
|
}'
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
<Expandable title="check response">
|
|
The credit system ID will be returned in the balance.
|
|
```json
|
|
{
|
|
"allowed": true,
|
|
"customerId": "user_123",
|
|
"requiredBalance": 0.01,
|
|
"balance": {
|
|
"featureId": "usd_credits",
|
|
"granted": 5,
|
|
"remaining": 5,
|
|
"usage": 0,
|
|
"unlimited": false,
|
|
"overageAllowed": false,
|
|
"nextResetAt": 1769110978704
|
|
}
|
|
}
|
|
```
|
|
</Expandable>
|
|
</Step>
|
|
|
|
<Step>
|
|
#### Tracking messages and using credits
|
|
|
|
Now let's implement our usage tracking and use up our credits. In this example, we're using 2 basic messages, which will cost us 0.02 USD credits.
|
|
|
|
<CodeGroup>
|
|
|
|
```typescript TypeScript
|
|
import { Autumn } from "autumn-js";
|
|
|
|
const autumn = new Autumn({ secretKey: "am_sk_test_1234" });
|
|
|
|
await autumn.customers.track({
|
|
customerId: "user_123",
|
|
featureId: "basic_messages",
|
|
value: 2,
|
|
});
|
|
```
|
|
|
|
```python Python
|
|
from autumn_sdk import Autumn
|
|
|
|
autumn = Autumn("am_sk_test_1234")
|
|
|
|
await autumn.customers.track(
|
|
customer_id="user_123",
|
|
feature_id="basic_messages",
|
|
value=2,
|
|
)
|
|
```
|
|
|
|
```bash cURL
|
|
curl -X POST "https://api.useautumn.com/v1/track" \
|
|
-H "Authorization: Bearer am_sk_test_1234" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"customer_id": "user_123",
|
|
"feature_id": "basic_messages",
|
|
"value": 2
|
|
}'
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
<Expandable title="track response">
|
|
```json
|
|
{
|
|
"customerId": "user_123",
|
|
"value": 2,
|
|
"balance": {
|
|
"featureId": "usd_credits",
|
|
"granted": 5,
|
|
"remaining": 4.98,
|
|
"usage": 0.02,
|
|
"unlimited": false,
|
|
"overageAllowed": false,
|
|
"nextResetAt": 1769110978704
|
|
}
|
|
}
|
|
```
|
|
</Expandable>
|
|
</Step>
|
|
|
|
<Step>
|
|
#### Upgrading to Pro
|
|
|
|
We can prompt the user to upgrade. When they click our "upgrade" button, we can use the `billing.attach` route to get a checkout URL for them to make a payment.
|
|
|
|
<CodeGroup>
|
|
|
|
```jsx React
|
|
import { useCustomer } from "autumn-js/react";
|
|
|
|
export default function UpgradeButton() {
|
|
const { attach } = useCustomer();
|
|
|
|
return (
|
|
<button onClick={() => attach({ planId: "pro" })}>
|
|
Upgrade to Pro
|
|
</button>
|
|
);
|
|
}
|
|
```
|
|
|
|
```typescript TypeScript
|
|
import { Autumn } from "autumn-js";
|
|
|
|
const autumn = new Autumn({ secretKey: "am_sk_test_1234" });
|
|
|
|
const response = await autumn.billing.attach({
|
|
customerId: "user_123",
|
|
planId: "pro",
|
|
});
|
|
|
|
// Redirect user to checkout
|
|
redirect(response.paymentUrl);
|
|
```
|
|
|
|
```python Python
|
|
from autumn_sdk import Autumn
|
|
|
|
autumn = Autumn("am_sk_test_1234")
|
|
|
|
response = await autumn.billing.attach(
|
|
customer_id="user_123",
|
|
plan_id="pro",
|
|
)
|
|
# Redirect user to response.payment_url
|
|
```
|
|
|
|
```bash cURL
|
|
curl -X POST "https://api.useautumn.com/v1/attach" \
|
|
-H "Authorization: Bearer am_sk_test_1234" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"customer_id": "user_123",
|
|
"plan_id": "pro"
|
|
}'
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
</Step>
|
|
|
|
<Step>
|
|
#### Purchasing Top-ups
|
|
|
|
When users run low on credits, they can purchase additional credits using our top-up plan. In this example, the user is purchasing 20 USD credits, which will cost them $20.
|
|
|
|
<CodeGroup>
|
|
|
|
```jsx React
|
|
import { useCustomer } from "autumn-js/react";
|
|
|
|
export default function TopUpButton() {
|
|
const { attach } = useCustomer();
|
|
|
|
return (
|
|
<button
|
|
onClick={() => attach({
|
|
planId: "top_up",
|
|
featureQuantities: [{
|
|
featureId: "usd_credits",
|
|
quantity: 20,
|
|
}],
|
|
})}
|
|
>
|
|
Buy More Credits
|
|
</button>
|
|
);
|
|
}
|
|
```
|
|
|
|
```typescript TypeScript
|
|
import { Autumn } from "autumn-js";
|
|
|
|
const autumn = new Autumn({ secretKey: "am_sk_test_1234" });
|
|
|
|
const response = await autumn.billing.attach({
|
|
customerId: "user_123",
|
|
planId: "top_up",
|
|
featureQuantities: [{
|
|
featureId: "usd_credits",
|
|
quantity: 20,
|
|
}],
|
|
});
|
|
|
|
redirect(response.paymentUrl);
|
|
```
|
|
|
|
```python Python
|
|
from autumn_sdk import Autumn
|
|
|
|
autumn = Autumn("am_sk_test_1234")
|
|
|
|
response = await autumn.billing.attach(
|
|
customer_id="user_123",
|
|
plan_id="top_up",
|
|
feature_quantities=[{
|
|
"feature_id": "usd_credits",
|
|
"quantity": 20,
|
|
}],
|
|
)
|
|
# Redirect to response.payment_url
|
|
```
|
|
|
|
```bash cURL
|
|
curl -X POST "https://api.useautumn.com/v1/attach" \
|
|
-H "Authorization: Bearer am_sk_test_1234" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"customer_id": "user_123",
|
|
"plan_id": "top_up",
|
|
"feature_quantities": [{
|
|
"feature_id": "usd_credits",
|
|
"quantity": 20
|
|
}]
|
|
}'
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
</Step>
|
|
</Steps>
|