Laravel Cashier

من موسوعة حسوب
(بالتحويل من Laravel/Cashier)

مقدمة

يقدم Laravel Cashier واجهة قويّة لاشتراكات خدمات الفواتير Stripe و Braintree، وهي تعالج تقريبًا كل شيفرات boilerplate الخاصة باشتراك الفواتير التي تخشى من كتابتها، وبالإضافة إلى إدارة الاشتراكات الأساسية، يستطيع Cashier التعامل مع القسائم (coupons)، ومبادلة الاشتراكات وكميّات الاشتراكات وفترات السماح بالإلغاء وحتى إنشاء ملفات PDF للفواتير.

تنبيه: إذا كنت تضع رسوم لمرة واحدةً فقط ولا توفّر الاشتراكات، فيجب عليك أن لا تستخدم Cashier، وبدلًا من ذلك، يجب عليك استخدام حزمة برمجيات التطوير (SDK) الخاصة بـ Braintree و Stripe مباشرةً.

الضبط

Stripe

Composer

أولًا، أضف حزمة Cashier الخاصة بخدمة Stripe إلى اعتماديّاتك:

composer require "laravel/cashier":"~7.0"

تهجير قواعد البيانات

قبل استخدام Cashier، سنحتاج أيضًا إلى إعداد قاعدة البيانات، وذلك عن طريق إضافة عدة أعمدة إلى جدول users وإنشاء جدول subscriptions جديد لاحتواء جميع اشتراكات عملائنا:

Schema::table('users', function ($table) {
    $table->string('stripe_id')->nullable();
    $table->string('card_brand')->nullable();
    $table->string('card_last_four')->nullable();
    $table->timestamp('trial_ends_at')->nullable();
});

Schema::create('subscriptions', function ($table) {
    $table->increments('id');
    $table->unsignedInteger('user_id');
    $table->string('name');
    $table->string('stripe_id');
    $table->string('stripe_plan');
    $table->integer('quantity');
    $table->timestamp('trial_ends_at')->nullable();
    $table->timestamp('ends_at')->nullable();
    $table->timestamps();
});

بمجرّد إنشاء التهجيرات، شغّل أمر migrate Artisan.

نموذج Billable

بعد ذلك، أضف سمة Billable إلى تعريف نموذجك، توفّر هذه السمة طرقًا مختلفة تسمح لك بتنفيذ مهام الفوترة الشائعة، كإنشاء الاشتراكات وتطبيق القسائم وتحديث بيانات بطاقة الائتمان:

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

مفاتيح الواجهة البرمجيّة API

ختامًا، يجب عليك ضبط مفتاح Stripe في ملف الضبط services.php، ويمكنك استرداد مفاتيح  Stripe API من لوحة تحكم Stripe:

'stripe' => [
    'model'  => App\User::class,
    'key' => env('STRIPE_KEY'),
    'secret' => env('STRIPE_SECRET'),
],

Braintree

تحذيرات Braintree

تعمل تطبيقات Stripe و Braintree بنفس الطريقة في عدة عمليات، فكلاهما يوفران فوترة الاشتراك ببطاقات الائتمان لكن يدعم Paypal Braintree أيضًا، ومع ذلك، يفتقر Braintree إلى بعض المميزات المدعومة من Stripe، ويجب عليك مراعاة ما يلي عند اتخاذ قرار استخدام Stripe أو Braintree:

  • يدعم Paypal Braintree على عكس Stripe.
  • لا يدعم Braintree توابع الزيادة والنقصان على الاشتراكات، وهذه حدود Braintree وليست حدود Cashier.
  • لا يدعم Braintree الخصومات القائمة على النسبة المئويّة، وهذه حدود Braintree وليست حدود Cashier.

Composer

أضف أولًا حزمة Cashier الخاصة بخدمة Braintree إلى اعتماديّاتك:

composer require "laravel/cashier-braintree":"~2.0"

مقدم الخدمة

بعد ذلك، سجّل مقدّم الخدمة Laravel\Cashier\CashierServiceProvider في ملف الضبط config/app.php :

Laravel\Cashier\CashierServiceProvider::class

قسيمة خطة الائتمان

قبل استخدام Cashier مع Braintree، ستحتاج إلى تعريف قسيمة plan-credit في لوحة تحكم Braintree، وسيُستخدم هذا الخصم لتجزئة الاشتراكات التي تتغيّر من الفوترة السنويّة إلى الشهريّة أو من الشهريّة إلى السنويّة.

يمكن أن يكون مقدار الخصم الذي تريد تكوينه في لوحة تحكم Braintree أي قيمة تريدها، حيث سيعيد Cashier تعريف المبلغ المحدد بالمبلغ المخصص الخاص بنا في كل مرة نطبّق فيها القسيمة، وهذه القسيمة مطلوبة لأن Braintree لا يدعم الاشتراكات التناسبية عبر ترددات الاشتراك.

تهجير قواعد البيانات

قبل استخدام Cashier، سنحتاج إلى إعداد قاعدة البيانات، وذلك عن طريق إضافة عدة أعمدة إلى جدول users و إنشاء جدول subscriptions جديد لاحتواء جميع اشتراكات عملائنا:

Schema::table('users', function ($table) {
    $table->string('braintree_id')->nullable();
    $table->string('paypal_email')->nullable();
    $table->string('card_brand')->nullable();
    $table->string('card_last_four')->nullable();
    $table->timestamp('trial_ends_at')->nullable();
});

Schema::create('subscriptions', function ($table) {
    $table->increments('id');
    $table->unsignedInteger('user_id');
    $table->string('name');
    $table->string('braintree_id');
    $table->string('braintree_plan');
    $table->integer('quantity');
    $table->timestamp('trial_ends_at')->nullable();
    $table->timestamp('ends_at')->nullable();
    $table->timestamps();
});

بمجرّد إنشاء التهجيرات، شغّل أمر migrate Artisan.

نموذج Billable

بعد ذلك، أضف سمة Billable إلى تعريف نموذجك:

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

مفاتيح الواجهة البرمجيّة API

ختامًا، يجب عليك ضبط الخيارات التاليّة في ملف الضبط services.php:

'braintree' => [
    'model'  => App\User::class,
    'environment' => env('BRAINTREE_ENV'),
    'merchant_id' => env('BRAINTREE_MERCHANT_ID'),
    'public_key' => env('BRAINTREE_PUBLIC_KEY'),
    'private_key' => env('BRAINTREE_PRIVATE_KEY'),
],

ثم يجب عليك إضافة استدعاءات حزمة برمجيات التطوير Braintree SDK التالية إلى تابع boot الخاص بموفّر الخدمة AppServiceProvider:

\Braintree_Configuration::environment(config('services.braintree.environment'));
\Braintree_Configuration::merchantId(config('services.braintree.merchant_id'));
\Braintree_Configuration::publicKey(config('services.braintree.public_key'));
\Braintree_Configuration::privateKey(config('services.braintree.private_key'));

ضبط العملة

العملة الافتراضية لـ Cashier هي الدولار الأمريكي (USD)، ويمكنك تغيير العملة الافتراضيّة باستدعاء تابع Cashier::useCurrency من داخل تابع boot من أحد موفرّي الخدمة.

يقبل تابع useCurrency سلستين نصيتيّن كمعاملات: العملة ورمز العملة:

use Laravel\Cashier\Cashier;

Cashier::useCurrency('eur', '€');

الاشتراكات

إنشاء الاشتراكات

لإنشاء اشتراك، استردّ نسخة من نموذج billable والتي ستكون عادةً نسخة من App\User، وبمجرد استرداد نسخة النموذج، يمكنك استخدام تابع newSubscription لإنشاء اشتراك النموذج:

$user = User::find(1);

$user->newSubscription('main', 'premium')->create($stripeToken);

يجب أن يكون المعامل الأول الممرّر إلى تابع newSubscription هو اسم الاشتراك، إذا كان تطبيقك يوفّر نوع واحد من الاشتراكات، يمكنك تسميته main أو primary، وأما المعامل الثاني فهو تحديد خطة Stripe أو Braintree التي يشترك فيها المستخدم، ويجب أن تتوافق هذه القيمة مع معرّف Stripe أو Braintree.

سيبدأ التابع create، والذي يقبل بطاقة ائتمان أو رمز مصدر Stripe، الاشتراك بالإضافة إلى تحديث قاعدة بياناتك بمعرّف العميل ومعلومات الفوترة ذات الصلة.

بيانات المستخدم إضافيّة

إذا كنت ترغب في تحديد تفاصيل إضافيّة للعميل، فيمكنك القيام بذلك عن طريق تمريرها كمعامل ثاني إلى التابع create:

$user->newSubscription('main', 'monthly')->create($stripeToken, [
    'email' => $email,
]);

لمعرفة المزيد حول الحقول الإضافية المدعومة بواسطة Stripe أو Braintree، راجع توثيق Stripe حول إنشاء العميل أو توثيق Braintree المشابهة.

القسائم

إذا رغبت في تطبيق قسيمة عند إنشاء الاشتراك، فيمكنك استخدام تابع withCoupon:

$user->newSubscription('main', 'monthly')
     ->withCoupon('code')
     ->create($stripeToken);

التحقق من حالة الاشتراك

بمجرد اشتراك المستخدم بتطبيقك، يمكنك التحقق بسهولة من حالة الاشتراك باستخدام مجموعة متنوعة من التوابع المناسبة، فالتابع subscribed يرجع true إذا كان للمستخدم اشتراك نشط، وحتى إذا كان الاشتراك حاليًا ضمن الفترة التجريبيّة:

if ($user->subscribed('main')) {
   //
}

إن التابع subscribed هو مرشح رائع لبرمجيّة وسيطة للمسار، فهو يسمح لك بترشيح الوصول إلى المسارات ووحدات التحكم بناءًا على حالة اشتراك المستخدم:

public function handle($request, Closure $next)
{
    if ($request->user() && ! $request->user()->subscribed('main')) {
       // This user is not a paying customer...
        return redirect('billing');
    }

    return $next($request);
}

إذا أردت تحديد ما إذا كان المستخدم ما يزال ضمن الفترة التجريبيّة، فيمكنك استخدام التابع onTrial، ويمكنك استخدام هذا التابع لعرض تحذير للمستخدم بأنه ما يزال في الفترة التجريبيّة الخاصة به:

if ($user->subscription('main')->onTrial()) {
   //
}

يمكن استخدام التابع subscribedToPlan لتحديد ما إذا كان المستخدم مشتركًا بخطة معينة بناءً على معرّف الخطة في Stripe / Braintree، وفي هذا المثال، سنحدد ما إذا كان الاشتراك main للمستخدم مفعّل للخطة الشهريّة:

if ($user->subscribedToPlan('monthly', 'main')) {
   //
}

حالة الاشتراك الملغى

لتحديد ما إذا كان المستخدم مشتركًا نشيطًا سابقًا ولكنه ألغى اشتراكه، يمكنك استخدام تابع cancelled:

if ($user->subscription('main')->cancelled()) {
   //
}

يمكنك أيضًا تحديد ما إذا كان المستخدم قد ألغى اشتراكه ولكنه لا يزال في فترة "السماح" حتى انتهاء صلاحيّة اشتراكه بالكامل، فعلى سبيل المثال، إذا الغى المستخدم اشتراكه في الخامس من شهر آذار/مارس والذي من المقرر في الأصل أن تنتهي صلاحيته في 10 من آذار/مارس، فسيكون المستخدم في فترة "السماح" حتى 10 من آذار/مارس، ولاحظ أن التابع subscribed سيرجع true في هذه الحالة:

if ($user->subscription('main')->onGracePeriod()) {
   //
}

تغيير الخطط

قد يرغب المستخدم بعد اشتراك في تطبيقك في تغييره إلى خطة اشتراك جديدة، ولاستبدال اشتراك جديد باشتراك المستخدم الحالي، مرّر معرّف الخطة إلى التابع swap:

$user = App\User::find(1);

$user->subscription('main')->swap('provider-plan-id');

إذا كان المستخدم قيد الإصدار التجريبي، فسيُحتفظ بالفترة التجريبيّة أيضًا وإذا كان هنالك كميّة للاشتراك، فسيُحتفظ بتلك الكميّة quantity أيضًا.

إذا أردت مبادلة الخطط وإلغاء أي فترة تجريبيّة للمستخدم قيد التشغيل حاليًا، فيمكنك استخدام التابع skipTrial:

$user->subscription('main')
        ->skipTrial()
        ->swap('provider-plan-id');

كميّة الاشتراك

تنبيه: كميّة الاشتراك مدعومة فقط من نسخة Stripe من Cashier، فلا يملك Braintree خاصيّة تتوافق مع "كميّة" Stripe.

تتأثر في بعض الأحيان الاشتراكات بالكميّة، فعلى سبيل المثال، إذا كان يتقاضى تطبيقك 10 دولارات شهريًا للمستخدم على الحساب، فلزيادة أو نقصان كميّة الاشتراك بسهولة، يمكنك استخدام التوابع incrementQuantity وdecrementQuantity:

$user = User::find(1);

$user->subscription('main')->incrementQuantity();

// Add five to the subscription's current quantity...
$user->subscription('main')->incrementQuantity(5);

$user->subscription('main')->decrementQuantity();

// Subtract five to the subscription's current quantity...
$user->subscription('main')->decrementQuantity(5);

يمكنك بدلًا من ذلك تعيين كميّة محددة باستخدام التابع updateQuantity:

$user->subscription('main')->updateQuantity(10);

يمكنك استخدام التابع noProrate لتحديث كميّة الاشتراك دون تقييم الرسوم:

$user->subscription('main')->noProrate()->updateQuantity(10);

للمزيد من المعلومات حول كميّات الاشتراك، راجع توثيق Stripe.

رسوم الاشتراك

لتحديد نسبة الرسوم للمستخدم على الاشتراك، طبّق التابع taxPercentage على نموذج billable الخاص بك، وأرجع قيمة رقميّة بين 0 و 100، مع عدم وجود أكثر من منزلتين عشريتين.

public function taxPercentage() {
    return 20;
}

يمكّنك التابع taxPercentage من تطبيق معدّل الرسوم على أساسيات نموذج بنموذج model-by-model، والذي قد يكون مفيدًا لقاعدة المستخدمين التي تغطي الكثير من البلدان ومعدلات الرسوم.

تنبيه: لا يطبّق التابع taxPercentage على رسوم الاشتراك فقط، فإذا استخدمت Cashier لتحصيل رسوم دفعة واحدة، فستحتاج إلى تعديد معدل الرسوم يدويًا في ذلك الوقت.

إلغاء الاشتراكات

لإلغاء اشتراك، استدع التابع cancel على اشتراك المستخدم:

$user->subscription('main')->cancel();

عند إلغاء الاشتراك، سيعيّن Cashier بشكل تلقائي عمود ends_at في قاعدة بياناتك، وسيُستخدم هذا العمود لمعرفة متى يجب على التابع subscribed إرجاع القيمة false، فعلى سبيل المثال، إذا ألغى عميل اشتراكه في الأول من مارس،ولم يكن من المقرر أن ينتهي الاشتراك حتى الخامس من مارس، فسيبقى التابع subscribed يرجع true حتى تاريخ 5 مارس.

يمكنك تحديد ما إذا كان المستخدم قد ألغى اشتراكه ولكن لا يزال في فترة "السماح" عن طريق استخدام التابع onGracePeriod:

if ($user->subscription('main')->onGracePeriod()) {
   //
}

إذا رغبت في إنهاء الاشتراك فورًا، فاستدع التابع cancelNow على اشتراك المستخدم:

$user->subscription('main')->cancelNow();

استئناف الاشتراكات

إذا ألغى مستخدم اشتراكه ويرغب في استئنافه استخدم التابع resume، ويجب على المستخدم أن يظل في فترة السماح لاستئناف اشتراكه:

$user->subscription('main')->resume();

إذا ألغى مستخدم اشتراكه ثم استأنفه قبل انتهاء صلاحيّة الاشتراك بالكامل فلن يحاسب على الفور، وبدلًا من ذلك، سيعاد تنشيط اشتراكه وسيحاسب على دورة الفوترة الأصليّة.

تحديث بطاقات الائتمان

يمكن استخدام التابع updateCard لتحديث معلومات بطاقة ائتمان العميل، ويقبل هذا التابع رمز Stripe وسيعيّن بطاقة الائتمان الجديدة المصدر الافتراضي للفوترة:

$user->updateCard($stripeToken);

الفترات التجريبيّة للاشتراكات

مع واجهة بطاقة الائتمان

إذا كنت ترغب في توفير فترات تجريبيّة لعملائك مع الاستمرار في جمع معلومات طريقة الدفع، يجب عليك استخدام التابع trialDays عند إنشائك للاشتراك:

$user = User::find(1);

$user->newSubscription('main', 'monthly')
            ->trialDays(10)
            ->create($stripeToken);

سيحدد هذا التابع تاريخ انتهاء الفترة التجريبيّة في سجل الاشتراك داخل قاعدة البيانات، بالإضافة إلى إرشاد Stripe أو Braintree إلى عدم البدء في إعداد فواتير للعميل إلى ما بعد هذا التاريخ.

تنبيه: إذا لم يلغي العميل اشتراكه قبل انتهاء الفترة التجريبيّة فسُتفرض الرسوم فور انتهاء الفترة التجريبيّة، لذلك يجب عليك التأكد من إبلاغ المستخدمين بتاريخ انتهاء الفترة التجريبيّة.

يتيح لك التابع trialUntil توفير نسخة DateTime لتحديد موعد انتهاء الفترة التجريبيّة:

use Carbon\Carbon;

$user->newSubscription('main', 'monthly')
            ->trialUntil(Carbon::now()->addDays(10))
            ->create($stripeToken);

يمكنك معرفة ما إذا كان المستخدم في فترته التجريبيّة أو لا إما باستخدام التابع onTrial على نسخة المستخدم أو باستخدام التابع onTrial على نسخة الاشتراك، فالمثالان التاليان متطابقان:

if ($user->onTrial('main')) {
   //
}

if ($user->subscription('main')->onTrial()) {
   //
}

بدون واجهة بطاقة الائتمان

إذا كنت ترغب في تقديم فترات تجريبيّة دون جمع معلومات عن طريقة دفع المستخدم، يمكنك تعيين عمود trial_ends_at في سجل المستخدم بناءً على تاريخ انتهاء الإصدار التجريبي المطلوب، ويتم ذلك عادةً أثناء تسجيل المستخدم:

$user = User::create([
   // ملأ بقية خصائص المستخدم
    'trial_ends_at' => now()->addDays(10),
]);

تنبيه: تأكد من إضافة تاريخ لـ trial_ends_at لتعريف النموذج.

يشير Cashier إلى نوع الفترة التجريبيّة على أنها "تجربة عامة"، نظرًا لأنه غير مرتبط بأي اشتراك حالي، وسيُرجع التابع onTrial في نسخة User القيمة true إذا لم يتجاوز التاريخ الحالي قيمة trial_ends_at:

if ($user->onTrial()) {
   // المستخدم في الفترة التجريبية
}

يمكنك أيضًا استخدام التابع onGenericTrial إذا أردت معرفة على وجه التحديد أن المستخدم ضمن فترة النسخة التجريبيّة العامة ولم ينشئ اشتراكًا فعليًّا بعد:

if ($user->onGenericTrial()) {
   //  المستخدم في الفترة التجريبية العامة
}

بمجرّد استعدادك لإنشاء اشتراك فعلي للمستخدم، يمكنك استخدام التابع newSubscription كالمعتاد:

$user = User::find(1);

$user->newSubscription('main', 'monthly')->create($stripeToken);

معالجة خطاطيف الويب في Stripe ‏

يمكن لكل من Stripe وBraintree إعلام تطبيقك بمجموعة من الأحداث عبر خطاطيف الويب (webhooks)، ولمعالجة خطاطيف الويب في Stripe، يجب عليك تعريف المسار الذي يشير إلى وحدة تحكم  خطّاف الويب الخاص بـ Cashier، وستُعالج وحدة التحكم هذه جميع طلبات خطّاف الويب الواردة وترسلها إلى تابع وحدة التحكم المناسب:

Route::post(
    'stripe/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

تنبيه: بمجرّد تسجيل الطريق الخاص بك، تأكد من تكوين عنوان خطاف ويب webhook URL في إعدادات لوحة تحكم Stripe.

بشكل افتراضي، ستتعامل وحدة التحكم تلقائيًا مع إلغاء الاشتراكات التي تحتوي على عدد كبير من محاولات الحصول على الرسوم الفاشلة (كما هو محدد بواسطة إعدادات Stripe)، ومع ذلك، كما سنكتشف قريبًا، يمكننا توسيع وحدة التحكم للتعامل مع أي حدث خطاف ويب ترغب به.

خطاطيف الويب وحماية CSRF

نظرًا لأن خطاطيف ويب Stripe تحتاج إلى تجاوز حماية CSRF الخاصة بإطار Laravel، تأكد من وضع عنوان URI كاستثناء في البرمجيّة الوسيطة  VerifyCsrfToken الخاصة بك أو ضع المسار خارج مجموعة البرمجيّة الوسيطة web:

protected $except = [
    'stripe/*',
];

تعريف معالجات أحداث خطاطيف الويب

سيتعامل Cashier بشكل تلقائي بإلغاء الاشتراك عند فشل شحن المبلغ، لكن إذا كان لديك أحداث خطاطيف ويب Stripe ترغب في معالجتها، فوسّع وحدة التحكم خطّاف الويب ويجب أن تكون أسماء التوابع متطابقة مع اتفاقيّة Cashier المتوقعة، والتي تنصّ على أنه يجب أن تبدأ بالكلمة handle وتستخدم حالة سِنَام الجمل (camelCase) لاسم خطّاف الويب الذي ترغب في معالجته. فعلى سبيل المثال، إذا كنت ترغب في معالجة خطاف الويب الخاص بالخطاف invoice.payment_succeeded فيجب عليك إضافة التابع handleInvoicePaymentSucceeded إلى وحدة التحكم:

<?php

namespace App\Http\Controllers;

use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    /**
     * التعامل مع خطاطيف الويب.
     *
     * @param  مصفوفة  $payload
     * @return Response
     */
    public function handleInvoicePaymentSucceeded($payload)
    {
       // معالجة الحدث
    }
}

بعد ذلك، حدد المسار إلى وحدة التحكم Cashier داخل ملف routes/web.php:

Route::post(
    'stripe/webhook',
    '\App\Http\Controllers\WebhookController@handleWebhook'
);

اشتراكات فاشلة

ماذا لو انتهت صلاحيّة بطاقة ائتمان العميل؟ لا تقلق! يحتوي Cashier على وحدة تحكم خطاف ويب يمكنها إلغاء اشتراك العميل بسهولة.

وكما ذُكِر أعلاه، كل ما عليك القيام به هو توجيه المسار إلى وحدة التحكم:

Route::post(
    'stripe/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

وهذا كل شيء، ستُلتقط المدفوعات الفاشلة وتُعالج عن طريق وحدة التحكم، وستلغي وحدة التحكم اشتراك العميل عندما يعلن Stripe فشل الاشتراك (عادةً بعد ثلاث محاولات دفع فاشلة).

معالجة خطاطيف الويب في Braintree

يمكن لكل من Stripe وBraintree إعلام تطبيقك بمجموعة من الأحداث عبر خطاطيف الويب، ولمعالجة خطاطيف ويب Braintree، يجب عليك تعريف المسار الذي يشير إلى وحدة تحكم خطّاف الويب الخاص بـ Cashier، وستعالج وحدة التحكم هذه جميع طلبات خطّاف الويب الواردة وترسلها إلى تابع وحدة التحكم المناسب:

Route::post(
    'braintree/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

تنبيه: بمجرّد تسجيل المسار الخاص بك، تأكد من إنشاء عنوان خطاف ويب webhook URL في إعدادات لوحة تحكم Braintree.

بشكل افتراضي، ستتعامل وحدة التحكم هذه تلقائيًا مع إلغاء الاشتراكات التي تحتوي على عدد كبير من محاولات الحصول على الرسوم الفاشلة (كما هو محدد بواسطة إعدادات Braintree)، ومع ذلك، كما سنكتشف قريبًا، يمكننا توسيع وحدة التحكم للتعامل مع أي حدث خطّاف ويب ترغب به.

خطاطيف الويب وحماية CSRF

نظرًا لأن خطاطيف ويب Braintree تحتاج إلى تجاوز حماية CSRF الخاصة بإطار Laravel، فتأكد من وضع URI كاستثناء في وسيطة VerifyCsrfToken الخاصة بك أو ضع مسار خارج مجموعة وسيطة web:

protected $except = [
    'braintree/*',
];

تعريف معالجات أحداث خطاطيف الويب

سيتعامل Cashier بشكل تلقائي بإلغاء الاشتراك عند فشل شحن المبلغ، لكن إذا كان لديك أحداث Braintree خطّاف ويب ترغب في معالجتها، فوسّع وحدة التحكم خطّاف الويب ويجب أن تكون أسماء التوابع متطابقة مع اتفاقيّة Cashier المتوقعة، والتي تنصّ على أنه يجب أن تبدأ بالكلمة handle وتستخدم حالة سِنَام الجمل (camelCase) لاسم خطّاف الويب الذي ترغب في معالجته. فعلى سبيل المثال، إذا كنت ترغب في التعامل مع خطّاف الويب dispute_opened فيجب عليك إضافة التابع handleDisputeOpened إلى وحدة التحكم:

<?php

namespace App\Http\Controllers;

use Braintree\WebhookNotification;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    /**
     * التعامل مع خطّافي  Braintree.
     *
     * @param  WebhookNotification  $webhook
     * @return Response
     */
    public function handleDisputeOpened(WebhookNotification $notification)
    {
       // Handle The Event
    }
}

اشتراكات فاشلة

ماذا لو انتهت صلاحيّة بطاقة ائتمان العميل؟ لا تقلق! يحتوي Cashier على وحدة التحكم خطّاف الويب الذي يمكنه إلغاء اشتراك العميل بسهول.

وكما ذكر أعلاه، كل ما عليك القيام به هو توجيه المسار إلى وحدة التحكم:

Route::post(
    'braintree/webhook',
    '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);

وهذا كل شيء، ستُلتقط المدفوعات الفاشلة وتُعالج عن طريق وحدة التحكم، وستلغى وحدة التحكم اشتراك العميل عندما يعرّف Braintree بفشل الاشتراك (عادةً بعد ثلاث محاولات دفع فاشلة). لا تنس أنك بحاجة إلى إعداد عنوان خطّاف الويب webhook URI في إعدادات لوحة تحكم Braintree الخاصة بك.

رسوم واحدة

رسم بسيط

تنبيه: عند استخدام Stripe، سيقبل التابع charge المقدار الذي ترغب في خصمه في أدنى مقام للعملة المستخدمة في طلب اشتراكك، ومع ذلك، عند استخدام Braintree، يجب عليك تمرير المبلغ الكامل بالدولار إلى التابع charge:

إذا كنت ترغب في إجراء دفعة واحدة لبطاقة ائتمان أحد العملاء المشتركين، فيمكنك استخدام التابع charge على نسخة نموذج Billable.

// centsتقبل الدفعات بال Stripe
$user->charge(100);

//Dollarsتقبل الدفعات بال Braitree
$user->charge(1);

يقبل التابع charge مصفوفة كمعامل ثاني مما يسمح لك بتمرير أي خيارات ترغب في تضمينها لرسوم إنشاء Stripe / Braintree، راجع توثيقات Stripe أو Braintree المتعلقة بالخيارات المتاحة لك عند إنشاء الرسوم:

$user->charge(100, [
    'custom_option' => $value,
]);

سيرمي التابع charge استثناء إذا فشلت عمليّة الشحن، وإذا نجحت، فستعاد إجابة Stripe أو Braintree كاملة من التابع:

try {
    $response = $user->charge(100);
} catch (Exception $e) {
   //
}

الشحن مع الفاتورة

قد تحتاج في بعض الأحيان إلى تحصيل رسوم لمرة واحدة ولكن ترغب أيضًا في إنشاء فاتورة لتحصيل الرسوم حتى تتمكن من تقديم إيصال بصيغة PDF إلى عميلك، يسمح لك التابع invoiceFor بذلك. فعلى سبيل المثال، لنفرض على العميل رسوم 5.00$ لمرة واحدة:

//  centsتقبل الدفعات بال Stripe

$user->invoiceFor('One Time Fee', 500);

//  dollarsتقبل الدفعات بال Braitree

$user->invoiceFor('One Time Fee', 5);

ستفرض الفاتورة على الفور على بطاقة ائتمان المستخدم، يقبل التابع invoiceFor مصفوفة كمعامل ثالث، مما يسمح لك بتمرير أي خيارات ترغب في تمريرها إلى Stripe أو Braintree عند إنشاء الرسم:

$user->invoiceFor('One Time Fee', 500, [
    'custom-option' => $value,
]);

إذا كنت تستخدم Braintree كموفّر للفوترة، يجب عليك تضمين خيار description عند استدعاء التابع invoiceFor:

$user->invoiceFor('One Time Fee', 500, [
    'description' => 'your invoice description here',
]);

سينشئ التابع invoiceFor فاتورة Stripe والتي ستعيد محاولات الفوترة الفاشلة، وإذا كنت لا ترغب في إنشاء فاتورة لإعادة المحاولة مع العمليات الفاشلة، ستحتاج إلى إغلاقها باستخدام واجهة Stripe البرمجية بعد أول عمليّة تحصيل رسوم فاشلة.

الفواتير

يمكنك استرداد مصفوفة من فواتير نموذج billable بسهولة باستخدام التابع invoices:

$invoices = $user->invoices();

// تضمين الفواتير المعلّقة في النتائج
$invoices = $user->invoicesIncludingPending();

عند إدراج الفواتير الخاصة بالعميل، يجوز لك استخدام التوابع المساعدة لعرض معلومات الفاتورة ذات الصلة، فعلى سبيل المثال، قد ترغب في سرد كل فاتورة في جدول مما يسمح للمستخدم تنزيل أي منها بسهولة:

<table>
    @foreach ($invoices as $invoice)
        <tr>
            <td>{{ $invoice->date()->toFormattedDateString() }}</td>
            <td>{{ $invoice->total() }}</td>
            <td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
        </tr>
    @endforeach
</table>

توليد فواتير PDF

من خلال المسار أو وحدة التحكم، استخدم التابع downloadInvoice لإنشاء PDF قابل للتحميل من الفاتورة، سينشئ هذا التابع استجابة HTTP المناسبة تلقائيًا لإرسال ملف التنزيل إلى المتصفح:

use Illuminate\Http\Request;

Route::get('user/invoice/{invoice}', function (Request $request, $invoiceId) {
    return $request->user()->downloadInvoice($invoiceId, [
        'vendor'  => 'Your Company',
        'product' => 'Your Product',
    ]);
});

مصادر