Skip to content

Commit 716e3d5

Browse files
committed
feat: update billing docs
1 parent acc95b6 commit 716e3d5

File tree

1 file changed

+80
-89
lines changed

1 file changed

+80
-89
lines changed

src/docs/utils/billing.md

Lines changed: 80 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Payments/Billing <Badge text="BETA - MVC Only" type="warning"/>
22

3-
Several projects require billing, be it for subscriptions, one-time payments, or donations. Leaf 4 now comes with billing support for Stripe and PayStack right out of the box. This makes integrating billing into your Leaf MVC project a breeze.
3+
Leaf MVC’s billing system helps makers ship faster by handling payments and subscriptions seamlessly. With built-in Stripe and Paystack support, you can set up one-time payments and recurring subscriptions in minutes—so you can focus on building, not billing.
44

55
## Setting up
66

@@ -42,39 +42,17 @@ This is all you have to do if you're planning to bill on-the-fly. Let's take a l
4242

4343
## Billing on-the-fly
4444

45-
Billing on-the-fly is the simplest way to bill a customer. This is useful for one-time payments, donations, and charging customers for services or cart items. Leaf takes care of everything for you, all you need to do is use Leaf billing to generate a payment link. This is the same whether for Blade, React, Vue, or Svelte since billing is done on the server.
45+
Billing on-the-fly is the fastest way to charge customers—perfect for one-time payments, donations, or services. Just generate a payment link with Leaf Billing, and we handle the rest. Whether you're using Blade, React, Vue, or Svelte, the process stays the same since billing runs on the server. No extra setup, just fast and seamless payments.
4646

4747
```php:no-line-numbers [MyController.php]
4848
...
4949
5050
public function handleCartPurchase($cartId) {
5151
$cart = Cart::find($cartId);
5252
53-
response()->redirect(
54-
billing()->instantLink([
55-
'amount' => $cart->total(),
56-
'currency' => 'USD',
57-
'description' => 'Purchase of items in cart',
58-
'metadata' => [
59-
'cart_id' => $cartId,
60-
'items' => $cart->items(),
61-
]
62-
])
63-
);
64-
}
65-
```
66-
67-
While this is incredibly simple, you would usually want to store other information about the payment session in your database. For those use-cases, you can use the `instantSession()` method.
68-
69-
```php:no-line-numbers [MyController.php]
70-
...
71-
72-
public function handleCartPurchase($cartId) {
73-
$cart = Cart::find($cartId);
74-
75-
$session = billing()->instantSession([
76-
'amount' => $cart->total(),
77-
'currency' => 'USD',
53+
$session = billing()->charge([
54+
'amount' => $cart->total(), // will auto convert to lowest currency unit
55+
'currency' => 'NGN',
7856
'description' => 'Purchase of items in cart',
7957
'metadata' => [
8058
'cart_id' => $cartId,
@@ -89,11 +67,11 @@ public function handleCartPurchase($cartId) {
8967
}
9068
```
9169

92-
This way, you can store the payment session in your database and use it to track the payment session. Either way, Leaf will automatically handle the payment session and will add data like the user who made the payment (if available), any metadata you added, and the payment status.
70+
Leaf takes care of the entire payment session for you—automatically tracking the user (if available), any metadata you provide, and the payment status, keeping your code clean and focused on your app.
9371

9472
## Billing Events/Webhooks
9573

96-
Once you have billed a customer, either on-the-fly or through a subscription, you would want to track the payment status, and webhook events are the best way to do this. Leaf billing comes with built-in support for webhooks. Leaf can automatically set up your webhooks for you using the `scaffold:billing-webhooks` command.
74+
Once you've charged a customer—whether with a one-time payment or a subscriptionyou’ll want to track the payment status. Webhooks are the best way to do this, and Leaf Billing has built-in support.
9775

9876
```bash:no-line-numbers
9977
php leaf scaffold:billing-webhooks
@@ -107,30 +85,29 @@ This will generate a `_billing_webhooks.php` file in your `routes` directory whi
10785
public function handle() {
10886
$event = billing()->webhook();
10987
110-
// Handle the event
111-
switch ($event->type) {
112-
case 'checkout.session.completed':
113-
// Payment was successful and the Checkout Session is complete
114-
// ✅ Give access to your service
115-
// $event->user() will give you the user who made the payment (if available)
116-
break;
117-
case 'checkout.session.expired':
118-
// Payment was not successful and the Checkout Session has expired
119-
// 📧 Maybe send an abandoned checkout mail?
120-
break;
121-
// ... handle all necessary events
122-
}
123-
```
88+
if ($event->is('checkout.session.completed')) {
89+
// Payment was successful and the Checkout Session is complete
90+
// ✅ Give access to your service
91+
// $event->user() will give you the user who made the payment (if available)
92+
return;
93+
}
12494
125-
<!-- TODO: change specific events to Leaf billing events like $event->type()->paymentSuccessful() -->
95+
if ($event->is('checkout.session.expired')) {
96+
// Payment was not successful and the Checkout Session has expired
97+
// 📧 Maybe send an abandoned checkout mail?
98+
return;
99+
}
126100
127-
This way, you can handle all billing events in one place, and you can easily track the payment status of your customers.
101+
// ... handle all necessary events
102+
}
103+
```
128104

129-
Since webhooks are stateless, you can't use the `session()` helper to get the user who made the payment...so we added a `webhook()` method to the billing instance to parse all data from the webhook event and create a `BillingEvent` instance which you can use to get the user who made the payment, the payment session, and all other necessary information.
105+
Since webhooks are stateless, you can't use the `session()` helper to retrieve the user who made the payment. To solve this, Leaf Billing provides a `webhook()` method on the billing instance which automatically parses the webhook, validates its source and generates a `BillingEvent` instance, giving you access to the user who made the payment, the payment session, and all other relevant details—effortlessly keeping your billing logic clean and efficient.
130106

131107
The billing event instance has the following items:
132108

133109
- `type`: The type of the event
110+
- `is()`: A method to check if the event is of a specific type
134111
- `user()`: The user who made the payment
135112
- `session()`: The payment session
136113
- `metadata`: The raw metadata of the payment session
@@ -143,33 +120,37 @@ For our example above, we can handle the payment event like this:
143120
public function handle() {
144121
$event = billing()->webhook();
145122
146-
// Handle the event
147-
switch ($event->type) {
148-
case 'checkout.session.completed':
149-
$cart = Cart::find($event->metadata['cart_id']);
150-
$cart->status = 'paid';
151-
$cart->save();
152-
break;
153-
case 'checkout.session.expired':
154-
$cart = Cart::find($event->metadata['cart_id']);
155-
$cart->status = 'abandoned';
156-
$cart->save();
157-
break;
158-
// ... handle all necessary events
123+
if ($event->is('checkout.session.completed')) {
124+
$cart = Cart::find($event->metadata['cart_id']);
125+
$cart->status = 'paid';
126+
$cart->save();
127+
128+
return;
129+
}
130+
131+
if ($event->is('checkout.session.expired')) {
132+
$cart = Cart::find($event->metadata['cart_id']);
133+
$cart->status = 'abandoned';
134+
$cart->save();
135+
136+
return;
137+
}
138+
139+
// ... handle all necessary events
159140
}
160141
```
161142

162143
For more information on billing events, you can check the [Stripe](https://stripe.com/docs/api/events/types) and [PayStack](https://paystack.com/docs/payments/webhooks/#types-of-events) documentation.
163144

164145
## Adding billing plans
165146

166-
While billing on-the-fly is great for one-time payments, you may want to set up subscriptions for your customers. Leaf billing allows you to set up billing plans for your customers. For this, you will have to set up your billing plans in your billing configuration file. Leaf scaffolding makes this easy with the `scaffold:billing-plans` command.
147+
While billing on-the-fly works for one-time payments, subscriptions need a structured setup. Leaf Billing makes this effortless—just run:
167148

168149
```bash:no-line-numbers
169150
php leaf scaffold:billing-plans
170151
```
171152

172-
This will generate a `config/billing.php` file with a `plans` key where you can set up your billing plans, which should look like this:
153+
This will generate a `config/billing.php` file with a `tiers` key where you can set up your billing plans, which should look like this:
173154

174155
```php:no-line-numbers [billing.php]
175156
...
@@ -214,9 +195,9 @@ You just need to add an array of tiers with the following keys:
214195
| `discount` | The discount percentage | `true` |
215196
| `features` | An array of features for the tier | `true` |
216197

217-
For the pricing, you can specify different prices for different durations. You can specify the price for `monthly`, `yearly`, `quarterly`, `weekly`, and `daily` durations. You only need to specify the price for the duration you want to use, if you want to create a one-time payment instead of a subscription, you can set the `price` instead of `price.duration`.
198+
You can set different prices for various durations`monthly`, `yearly`, `quarterly`, `weekly`, or even `daily`. Just define the ones you need. For one-time payments, simply set `price` instead of `price.duration`.
218199

219-
Besides this, you also get a pricing component based on your view engine. This component is available in Blade, React, Vue, and Svelte, and you can use it to display your billing plans.
200+
Plus, Leaf provides a built-in pricing component for Blade, React, Vue, and Svelte, so you can display your plans effortlessly in your app.
220201

221202
::: code-group
222203

@@ -256,7 +237,7 @@ import Pricing from '@/components/pricing.svelte';
256237

257238
## Billing Subscriptions
258239

259-
Once you have set up your billing plans in the billing config, your users can subscribe to these plans. The `scaffold:billing-plans` command also generates a `BillingPlans` controller with a `subscribe()` method that you can use to subscribe your users to a plan.
240+
After setting up your billing plans in the config, users can subscribe effortlessly. The `scaffold:billing-plans` command also generates a BillingPlans controller with a `subscribe()` method, making it easy to enroll users into a plan with minimal setup.
260241

261242
```php:no-line-numbers{4} [BillingPlansController.php]
262243
...
@@ -270,31 +251,34 @@ public function subscribe($plan) {
270251
}
271252
```
272253

273-
This will redirect your users to the billing provider's checkout page where they can subscribe to the plan. Once they have subscribed, you can track their subscription status using the webhook events. The `scaffold:billing-plans` command also updates the `BillingWebhooks` controller to handle subscription events.
254+
This redirects users to the billing providers checkout page to subscribe. Once subscribed, you can track their status via webhook events. The `scaffold:billing-plans` command also updates the BillingWebhooks controller to handle subscription events seamlessly.
274255

275256
```php:no-line-numbers [BillingWebhooksController.php]
276257
...
277258
278259
public function handle() {
279260
$event = billing()->webhook();
280261
281-
// Handle the event
282-
switch ($event->type) {
283-
case 'checkout.session.completed':
284-
$event->user()->newSubscription();
285-
// update other models if needed
286-
break;
287-
case 'checkout.session.expired':
288-
UserMailer::sendAbandonedCheckoutMail($event->user());
289-
break;
290-
case 'customer.subscription.deleted':
291-
// Subscription was deleted
292-
$event->user()->cancelSubscription();
293-
break;
262+
if ($event->is('checkout.session.completed')) {
263+
$event->user()->newSubscription();
264+
return;
265+
}
266+
267+
if ($event->is('customer.subscription.deleted')) {
268+
$event->user()->cancelSubscription();
269+
return;
270+
}
271+
272+
if ($event->is('checkout.session.expired')) {
273+
UserMailer::sendAbandonedCheckoutMail($event->user());
274+
return;
275+
}
276+
277+
// ... handle all necessary events
294278
}
295279
```
296280

297-
`$event->user()` returns an instance of Leaf's auth user, which automatically gets the `HasBilling` trait once Leaf billing is installed. This trait allows you to easily check the user's subscription status, plan, and other billing information directly on the user. `$event->user()` automatically attaches the billing tier information to the user object, which is why we can call `newSubscription()` and `cancelSubscription()` without any arguments in the example above.
281+
`$event->user()` returns an instance of Leaf's auth user, enhanced with the `HasBilling` trait when Leaf Billing is installed. This lets you effortlessly check a user's subscription status, plan, and billing details. Since the billing tier is automatically attached, methods like `newSubscription()` and `cancelSubscription()` work without extra arguments.
298282

299283
## Checking billing status
300284

@@ -552,15 +536,22 @@ As with all payment systems, you need to ensure that your billing system is secu
552536
- In your [Developers], copy your public & private keys and add them to `BILLING_SECRET_KEY` & `BILLING_PUBLISHABLE_KEY` in your production environment variables.
553537
- In your [Developers], [Webhook], [Add Enpoint]. Set `<your-domain>/billing/webhook`. Select [checkout.session.completed] event (or more if needed). Copy the signing secret and add it to `BILLING_WEBHOOK_SECRET` in your production environment variables.
554538

555-
::: warning CSRF Protection
556-
If you are using the default CSRF config, then your `/billing/webhook` route is already excluded from CSRF protection, however, if you maintain your own CSRF config in `config/csrf.php`, you should exclude the `/billing/webhook` route from CSRF protection.
539+
## Security
557540

558-
```php:no-line-numbers [csrf.php]
559-
...
560-
'except' => [
561-
'/billing/webhook',
562-
],
563-
...
564-
```
541+
When using Leaf billing, you need to ensure that your billing system is secure. Here are a few things to check:
565542

566-
:::
543+
- Billing Webhooks
544+
545+
Always make sure that your `BILLING_WEBHOOK_SECRET` is set in your `.env` file. This secret is used to verify that the webhook is coming from your billing provider. You can get this secret from your billing provider's dashboard. Once you set this secret, Leaf billing will automatically verify that the webhook is coming from your billing provider.
546+
547+
- CSRF Protection
548+
549+
If you are using the default CSRF config, then your `/billing/webhook` route is already excluded from CSRF protection, however, if you maintain your own CSRF config in `config/csrf.php`, you should exclude the `/billing/webhook` route from CSRF protection.
550+
551+
```php:no-line-numbers [csrf.php]
552+
...
553+
'except' => [
554+
'/billing/webhook',
555+
],
556+
...
557+
```

0 commit comments

Comments
 (0)