Signup already captures a click ID per ad network -- gclid, fbclid, li_fat_id, ttclid, rdt_cid, epik -- but only the UTM parameters reached the subscription. UTMs say which campaign a customer came from; the click ID identifies the individual click, which is what Google and Meta need to match a subscription back to the ad that produced it. Every key is spelled out at the call site, so what reaches Stripe is readable in one place without following a constant. Click IDs are text columns on purpose, since truncating them at 255 would destroy the value. Stripe caps a metadata value at 500 characters and rejects the request rather than truncating, so an unbounded column reaching it would fail the whole checkout and the customer could not subscribe at all. Values are cut to 500 before they are sent. The UTM columns are varchar(255) and cannot reach it; this exists for the text ones.
68 lines
2.6 KiB
PHP
68 lines
2.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Actions\Billing;
|
|
|
|
use App\Models\Account;
|
|
use App\Support\Billing\ConfigureSubscriptionCheckout;
|
|
use Illuminate\Support\Str;
|
|
use Inertia\Inertia;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class StartSubscriptionCheckout
|
|
{
|
|
/**
|
|
* Create a Stripe Checkout session for the given price and return an Inertia
|
|
* redirect to it. Quantity tracks the account's workspace count. Trial days,
|
|
* optional first-month coupon, and promotion codes come from cashier /
|
|
* trypost billing env config via ConfigureSubscriptionCheckout. The owner's
|
|
* signup attribution -- UTM parameters and ad click IDs -- and onboarding
|
|
* answers ride along as subscription metadata, flattened to the strings
|
|
* Stripe stores and cut to the 500 characters it allows per value. Stripe
|
|
* rejects a longer value outright rather than truncating it, which would
|
|
* fail the whole checkout: https://docs.stripe.com/api/metadata
|
|
*/
|
|
public function redirect(Account $account, string $priceId, string $cancelUrl): Response
|
|
{
|
|
$account->createOrGetStripeCustomer([
|
|
'email' => $account->stripeEmail(),
|
|
'name' => $account->stripeName(),
|
|
]);
|
|
|
|
$owner = $account->owner;
|
|
|
|
$metadata = array_filter([
|
|
'utm_source' => $owner?->utm_source,
|
|
'utm_medium' => $owner?->utm_medium,
|
|
'utm_campaign' => $owner?->utm_campaign,
|
|
'utm_term' => $owner?->utm_term,
|
|
'utm_content' => $owner?->utm_content,
|
|
'gclid' => $owner?->gclid,
|
|
'fbclid' => $owner?->fbclid,
|
|
'li_fat_id' => $owner?->li_fat_id,
|
|
'ttclid' => $owner?->ttclid,
|
|
'rdt_cid' => $owner?->rdt_cid,
|
|
'epik' => $owner?->epik,
|
|
'persona' => $owner?->persona?->value,
|
|
'goals' => implode(',', $owner?->goals ?? []),
|
|
'referral_source' => $owner?->referral_source?->value,
|
|
]);
|
|
|
|
$subscription = $account->newSubscription(Account::SUBSCRIPTION_NAME, $priceId)
|
|
->quantity(max(1, $account->workspaces()->count()))
|
|
->withMetadata(array_map(
|
|
fn (string $value): string => Str::limit($value, 500, ''),
|
|
$metadata,
|
|
));
|
|
|
|
ConfigureSubscriptionCheckout::apply($subscription, $account);
|
|
|
|
$session = $subscription->checkout([
|
|
'success_url' => route('app.billing.processing').'?session_id={CHECKOUT_SESSION_ID}',
|
|
'cancel_url' => $cancelUrl,
|
|
]);
|
|
|
|
return Inertia::location($session->url);
|
|
}
|
|
}
|