پشت پردهی صورتحساب متری با Paddle
خلاصهٔ کاملتر
وقتی Phare شروع به رشد ارگانیک کرد و مشتریهای جدید از سراسر دنیا شروع به ثبتنام کردن، مسئلهی VAT compliance جدی شد. نیکلا بووه تصمیم گرفت از Stripe به Paddle مهاجرت کنه چون Paddle به عنوان merchant-of-record کل مسئولیت مالیات رو برعهده میگیره. اما اجرای درست صورتحساب متری روی Paddle، خیلی از چیزی که در داکیومنتیشن نوشته بود سختتر بود.
مدل قیمتگذاری Phare سادهست: یه پلن رایگان و یه پلن پولی ۵ یورو در ماه که شامل سهمیهی پایه برای رویدادهای مانیتورینگ، هشدارهای تلفنی و خلاصههای هوش مصنوعی میشه. مشتریها میتونن سهمیهی بیشتر بخرن و هزینهی اضافه رو بپردازن. نیکلا همچنین میخواست فاکتورها بر اساس ماههای تقویمی (مثلاً فاکتور بهمن = هزینههای بهمن) صادر بشن، نه بر اساس تاریخ سالگرد اشتراک هر مشتری.
اولین مشکل جدی اینجا بود که وقتی میخوای یه transaction برای subscription ایجاد کنی، پارامترهای billing cycle نادیده گرفته میشن. راهحل اول که تغییر billing cycle بعد از checkout بود، تجربهی کاربری بدی داشت: کاربر بلافاصله بعد از پرداخت یه ایمیل میگرفت که «چرخهی صورتحساب شما تغییر کرد!»
راهحل نهایی استفاده از zero-value subscription بود؛ یه قابلیت کممستند که فقط از طریق API قابل ساخته،شدنه. این نوع subscription هم billing cycle رو درست اعمال میکنه، هم اطلاعات قیمت و تاریخ تمدید رو از چکاوت iframe پنهان میکنه تا بشه اطلاعات دلخواه رو نشون داد. کد ساخت transaction با PHP SDK اینطوری میشه:
$paddle->transactions->create(new CreateTransaction(
items: [new TransactionCreateItemWithPrice(
price: new TransactionNonCatalogPrice(
description: 'Monthly Scale plan subscription',
unitPrice: new Money('0', CurrencyCode::EUR()),
billingCycle: new TimePeriod(Interval::Month(), 1),
taxMode: TaxMode::External(),
),
quantity: 1,
)],
customerId: $customerId,
currencyCode: CurrencyCode::EUR(),
collectionMode: CollectionMode::Automatic(),
billingPeriod: new TransactionTimePeriod(
startsAt: $startAt->toDateTime(),
endsAt: $endAt->toDateTime(),
)
));اما zero-value subscription یه مشکل بزرگ داره: اگه مشتری اشتراکش رو لغو کنه، لغو فوریه و هیچ راهی برای دریافت هزینهی مصرف همون ماه نیست. برای حل این مشکل، باید subscription را به یه subscription معمولی (پولی) تبدیل کرد. این کار با یه API call انجام میشه:
$paddle->subscriptions->update($subscriptionId, new UpdateSubscription(
items: [new SubscriptionItems($plan->paddlePriceId, quantity: 1)],
prorationBillingMode: ProrationBillingMode::ProratedImmediately(),
));اگه مشتری بیشتر از ۷ روز تا پایان ماه مونده باشه، مبلغ پرورِیتشده فوراً دریافت میشه؛ وگرنه برای جلوگیری از دریافت مبلغ ناچیز درست بعد از checkout، به فاکتور بعدی موکول میشه.
از اونجایی که Paddle برخلاف Stripe هیچ invoice grace period داخلیای نداره، نیکلا یه سیستم زمانبندی دستساز طراحی کرده. Paddle 30 دقیقه قبل از پردازش پرداخت، فاکتورها رو فریز میکنه. چون همهی subscriptions روی ساعت 12:00 UTC اول هر ماه تنظیم شدن، یه پنجرهی زمانی راحت برای گزارش مصرف وجود داره. تایملاین اول هر ماه اینه: ساعت 00:00 رکوردهای مصرف ماه جدید ساخته میشن، ساعت 01:00 هزینههای اضافه محاسبه و به Paddle گزارش میشن، ساعت 11:30 Paddle دیگه تغییر نمیپذیره، و ساعت 12:00 پرداختها گرفته میشن.
در نهایت این پیادهسازی یه سیستم کامل صورتحساب متری با دورهی تقویمی رو روی Paddle ممکن میکنه، ولی هزینهاش اینه که باید خیلی از چیزها رو از صفر بسازی؛ چیزهایی که Stripe به صورت آماده داره.
نکات کلیدی:
- Paddle بر خلاف Stripe نه Meter API داره، نه grace period، نه جمعآوری خودکار مصرف
- billing cycle در transaction معمولی نادیده گرفته میشه؛ راهحل: zero-value subscription
- zero-value subscription برای لغو اشتراک مشکل داره و باید به subscription پولی تبدیل بشه
- invoice grace period باید به صورت دستی با زمانبندی دقیق ساخته بشه
- Paddle فاکتورها رو ۳۰ دقیقه قبل از پرداخت فریز میکنه که پنجرهی گزارش مصرف رو محدود میکنه
- هزینههای اضافه به عنوان one-time charge روی subscription اضافه میشن




