×ðÁú¿­Ê±

Laravel¿ª·¢£ºÔõÑùʹÓÃLaravel CashierʵÏÖ¶©ÔĹ¦Ð§£¿

laravel¿ª·¢£ºÔõÑùʹÓÃlaravel cashierʵÏÖ¶©ÔĹ¦Ð§£¿

Laravel CashierÊÇÒ»¸öÒ×ÓÚʹÓõÄStripe¸¶¿î´¦Öóͷ£°ü£¬¿ÉÒÔ×ÊÖúÎÒÃÇÔÚLaravelÓ¦ÓóÌÐòÖÐʵÏÖ¶©ÔĹ¦Ð§¡£Ôڴ˽̳ÌÖУ¬ÎÒÃǽ«Ñ§Ï°ÔõÑùʹÓÃLaravel CashierʵÏÖ¶©ÔĹ¦Ð§¡£

°ì·¨1£º×°ÖÃLaravel Cashier

ÔÚÎÒÃÇ×îÏÈʹÓÃLaravel Cashier֮ǰ£¬ÐèҪװÖÃËü¡£ÔÚLaravelÏîÄ¿ÖÐÔËÐÐÒÔÏÂÏÂÁîÀ´×°ÖÃLaravel Cashier£º

composer require laravel/cashier

µÇ¼ºó¸´ÖÆ

°ì·¨2£ºÉèÖÃStripeƾ֤

Laravel CashierÐèÒªÄúÉèÖÃStripeƾ֤²Å»ªÕý³£ÊÂÇé¡£ÈôÊÇÄú»¹Ã»ÓÐStripeÕË»§£¬¿ÉÒÔÔÚhttps://stripe.com/ÉÏ×¢²áÒ»¸öÐÂÕË»§¡£È»ºó£¬µÇ¼µ½Stripe Dashboard£¬µã»÷APIÑ¡Ï£¬¸´ÖÆÄúµÄ˽Կ²¢½«ÆäÌí¼Óµ½.envÎļþÖС£

STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_PUBLIC_KEY=your_stripe_public_key

µÇ¼ºó¸´ÖÆ

°ì·¨3£º½¨É趩ÔÄÍýÏë

ÔÚÎÒÃÇʵÏÖ¶©ÔĹ¦Ð§Ö®Ç°£¬ÎÒÃÇÐèÒªÔÚStripe DashboardÖн¨É趩ÔÄÍýÏ롣תµ½ÄúµÄStripe Dashboard²¢½¨ÉèÒ»¸öеĶ©ÔÄÍýÏë¡£

ÔÚ½¨É趩ÔÄÍýÏëʱ£¬ÇëÈ·±£ÉèÖÃÊʵ±µÄ½ð¶î£¬¼Æ·ÑÖÜÆÚºÍÊÔÓÃÆÚ£¨ÈôÓÐÐèÒª£©¡£

°ì·¨4£º½¨ÉèSubscriberÄ£×Ó

SubscriberÄ£×Ó½«¼ÌÐøLaravel CashierµÄBillable Trait£¬²¢½ç˵Óйض©ÔĵÄËùÓÐÐÅÏ¢¡£Í¨¹ýÖ´ÐÐÒÔÏÂÏÂÁîÔÚLaravelÏîÄ¿Öн¨ÉèSubscriberÄ£×Ó£º

php artisan make:model Subscriber -m

µÇ¼ºó¸´ÖÆ

´ËÏÂÁ½¨ÉèÒ»¸öSubscriberÄ£×ÓÎļþºÍÒ»¸öǨáãÎļþ¡£

ÔÚǨáãÎļþÖУ¬ÎÒÃÇÐèÒª½«Billable TraitÌí¼Óµ½Ä£×ÓÖС£ÐÞ¸ÄSubscriberÄ£×ÓǨáãÎļþ£¬ÈçÏÂËùʾ£º

<?php
use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;
class CreateSubscribersTable extends Migration
{
    public function up()
    {
        Schema::create('subscribers', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->string('stripe_id')->nullable();
            $table->string('card_brand')->nullable();
            $table->string('card_last_four')->nullable();
            $table->timestamp('trial_ends_at')->nullable();
            $table->timestamps();
        });
        Schema::table('subscribers', function (Blueprint $table) {
            $table->softDeletes();
        });
    }
    public function down()
    {
        Schema::table('subscribers', function (Blueprint $table) {
            $table->dropSoftDeletes();
        });
        Schema::dropIfExists('subscribers');
    }
}

µÇ¼ºó¸´ÖÆ

ÎÒÃÇÔÚSubscriberÄ£×ÓÖÐÌí¼ÓÁ˶©ÔÄËùÐèµÄËùÓÐ×Ö¶ÎÒÔ¼°Èíɾ³ýÖ§³Ö¡£

°ì·¨5£ºÉèÖÃLaravel Cashier

ÔÚSubscriberÄ£×ÓÖÐÌí¼ÓLaravel Cashier Trait£¬ÈçÏÂËùʾ£º

<?php
namespace App;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;
use IlluminateDatabaseEloquentSoftDeletes;
use LaravelCashierBillable;
class Subscriber extends Authenticatable
{
    use Notifiable, SoftDeletes, Billable;
    protected $dates = ['deleted_at'];
    protected $fillable = [
        'name', 'email', 'password',
    ];
    protected $hidden = [
        'password', 'remember_token',
    ];
    public function getStripeKeyName()
    {
        return 'stripe_id';
    }
}

µÇ¼ºó¸´ÖÆ

ÔÚÉÏÃæµÄ´úÂëÖУ¬ÎÒÃÇÌí¼ÓÁËBillable Trait²¢Ö¸¶¨ÁËStripeÖеġ°stripe_id¡±¡£

°ì·¨6£ºÊµÏÖ¶©ÔĹ¦Ð§

ÏÖÔÚ£¬ÎÒÃÇÒѾ­ÉèÖúÃÁËLaravel Cashier²¢½¨ÉèÁËSubscriberÄ£×Ó£¬ÎÒÃÇ¿ÉÒÔ×îÏÈʹÓÃLaravel CashierÀ´ÊµÏÖ¶©ÔĹ¦Ð§¡£

ÔÚÎÒÃÇ×îÏÈ֮ǰ£¬ÎÒÃÇÐèҪȷ±£Óû§ÒѵǼ¡£ÎÒÃǽ«Ê¹ÓÃLaravelµÄAuthentication¹¦Ð§À´´¦Öóͷ£´Ë°ì·¨¡£

ÔÚweb.phpÖÐÌí¼ÓÒÔÏ·ÓÉ£º

Route::group(['middleware' => ['auth']], function () {
    Route::get('/subscribe', 'SubscriptionController@index')->name('subscribe.index');
    Route::post('/subscribe', 'SubscriptionController@store')->name('subscribe.store');
    Route::get('/subscribe/cancel', 'SubscriptionController@cancel')->name('subscribe.cancel');
    Route::get('/subscribe/resume', 'SubscriptionController@resume')->name('subscribe.resume');
    Route::get('/subscribe/invoice', 'SubscriptionController@invoice')->name('subscribe.invoice');
});

µÇ¼ºó¸´ÖÆ

ÏÖÔÚ£¬ÎÒÃÇÐèÒªÌí¼ÓÒ»¸öSubscriptionControllerÀ´´¦Öóͷ£×ðÁú¿­Ê±¶©ÔĹ¦Ð§¡£Ê¹ÓÃÒÔÏÂÏÂÁÉèSubscriptionController£º

php artisan make:controller SubscriptionController

µÇ¼ºó¸´ÖÆ

ÔÚSubscriptionControllerÖÐÌí¼ÓindexÒªÁ죬

<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use LaravelCashierExceptionsIncompletePayment;
use Stripe{EphemeralKey, PaymentIntent, PaymentMethod};
class SubscriptionController extends Controller
{
    public function index(Request $request)
    {
        $intent = $request->user()->createSetupIntent();
        return view('subscription.index', [
            'intent' => $intent,
        ]);
    }
}

µÇ¼ºó¸´ÖÆ

ÔÚÉÏÃæµÄ´úÂëÖУ¬ÎÒÃǽ¨ÉèÁËÒ»¸öSetup IntentÓÃÓÚÍøÂçÓû§µÄ¸¶¿îÐÅÏ¢¡£È»ºó£¬ÎÒÃǽ«´ËSetup Intentת´ïµ½ÊÓͼÖУ¬²¢ÔÚÓÃÓÚ¶©ÔÄµÄ±íµ¥ÖÐʹÓÃËü¡£

ÔÚ¶©ÔÄ±íµ¥ÖУ¬ÎÒÃÇ¿ÉÒÔʹÓÃStripe.jsÀ´ÍøÂçÓû§µÄ¸¶¿îÐÅÏ¢¡£ÒÔÏÂÊÇÒ»¸ö»ù±¾µÄ¶©ÔÄ±íµ¥Ê¾Àý£º

@extends('layouts.app')
@section('content')
{!! Form::open(['route' => 'subscribe.store', 'class' => 'form-horizontal']) !!}
<div class="form-group row">
    <label for="name">{{ __('Plan') }}</label>
    <select name="plan" id="plan" class="form-control">
        <option value="price_123456">Basic Plan - $20/month</option>
        <option value="price_123457">Standard Plan - $50/month</option>
        <option value="price_123458">Premium Plan - $100/month</option>
    </select>
    <div id="card-element"></div>
</div>
<div class="form-group row">
    <div id="card-errors" class="mx-auto" role="alert"></div>
</div>
<div class="form-group row mb-0">
    <button type="submit" class="btn btn-primary">
        {{ __('Subscribe') }}
    </button>
</div>
{!! Form::close() !!}
<script src="https://js.stripe.com/v3/"></script>
<script>
    let stripe = Stripe('{{ env('STRIPE_PUBLIC_KEY') }}');
    let elements = stripe.elements();
    let card = elements.create('card', {
            hidePostalCode: true,
            style: {
            base: {
                iconColor: '#666EE8',
                color: '#31325F',
                lineHeight: '40px',
                fontWeight: 300,
                fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
                fontSize: '15px',
                '::placeholder': {
                    color: '#CFD7E0',
                },
            },
            },
        }
    );
    card.mount('#card-element');
    let cardErrors = document.getElementById('card-errors');
        card.addEventListener('change', function(event) {
        if (event.error) {
            cardErrors.textContent = event.error.message;
        } else {
            cardErrors.textContent = '';
        }
    });
    let submitButton = document.querySelector('button[type=submit]');
    let form = document.querySelector('form');
    let clientSecret = null;
    submitButton.addEventListener('click', async function(ev) {
        ev.preventDefault();
        submitButton.disabled = true;
        let {setupIntent, error} = await stripe.confirmCardSetup(
            clientSecret, {
            payment_method: {
                card: card,
            }
        });
        if (error) {
            cardErrors.textContent = error.message;
            submitButton.disabled = false;
            return;
        }
        let paymentMethodId = setupIntent.payment_method;
        axios.post('{{ route('subscribe.store') }}', {
            plan: document.querySelector('#plan').value,
            payment_method: paymentMethodId,
            _token: '{{ csrf_token() }}'
        }).then(function(response) {
            window.location.href = response.data.success_url;
        }).catch(function(error) {
            submitButton.disabled = false;
        });
    });
    (async () => {
        let {setupIntent} = await axios.get('/api/create-setup-intent', {
            headers: {
                'X-CSRF-Token': document.head.querySelector('meta[name="csrf-token"]').content
            }
        }).then(response => response.data)
        clientSecret = setupIntent.client_secret;
    })();
</script>
@endsection

µÇ¼ºó¸´ÖÆ

ÔÚÉÏÃæµÄ´úÂëÖУ¬ÎÒÃÇʹÓÃÁËStripe.jsÀ´¹¹½¨Ò»¸ö¼òÆӵĶ©ÔÄ±íµ¥¡£ÎÒÃÇͨ¹ýʹÓÃStripe.jsÖеÄcreate()ÒªÁ콨ÉèÒ»¸öcardÔªËØ£¬È»ºó½«Æä¹ÒÔص½#card-element divÖС£ÕâÀïÎÒÃÇ»¹Ê¹ÓÃÁËaxiosÀ´´¦Öóͷ£POSTÇëÇó£¬ÆäÖаüÀ¨¶©ÔÄÍýÏëÐÅÏ¢ºÍ¸¶¿îÐÅÏ¢¡£ÈôÊǸ¶¿î±»Àֳɴ¦Öóͷ££¬ÎÒÃǽ«Öض¨ÏòÓû§µ½ÀÖ³ÉÒ³Ãæ¡£

ÏÖÔÚ£¬ÎÒÃÇÐèÒªÌí¼Ó´æ´¢Óû§¸¶¿îÐÅÏ¢ºÍ½¨É趩ÔÄÂß¼­µÄ´æ´¢¿â¡£

<?php
namespace AppRepositories;
use AppSubscriber;
use IlluminateSupportFacadesConfig;
use LaravelCashierExceptionsPaymentActionRequired;
use LaravelCashierExceptionsPaymentFailure;
use LaravelCashierSubscription;
use StripeStripe;
class SubscriberRepository
{
    public function createFromRequest(array $data)
    {
        $subscriber = Subscriber::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
        $subscriber->createAsStripeCustomer([
            'name' => $subscriber->name,
            'email' => $subscriber->email,
        ]);
        return $subscriber;
    }
    public function subscribe(Subscriber $subscriber, string $plan, string $paymentMethod)
    {
        $subscriber->newSubscription('main', $plan)->create($paymentMethod, [
            'email' => $subscriber->email,
        ]);
    }
}

µÇ¼ºó¸´ÖÆ

ÔÚÉÏÃæµÄ´úÂëÖУ¬ÎÒÃǽ¨ÉèÁËÒ»¸öSubscriberRepositoryÀ࣬ÓÃÓÚ´æ´¢Óû§ÐÅÏ¢ºÍ½¨É趩ÔÄ¡£ÔÚ×ðÁú¿­Ê±createFromRequest()ÒªÁìÖУ¬ÎÒÃǽ«½¨ÉèÒ»¸öеÄStripe Customer£¬È»ºó½«ÆäÏà¹ØÐÅÏ¢´æ´¢µ½×ðÁú¿­Ê±SubscriberÄ£×ÓÖС£ÔÚsubscribe()ÒªÁìÖУ¬ÎÒÃÇʹÓÃLaravel CashierµÄnewSubscription()º¯ÊýÀ´ÎªÓû§½¨ÉèеĶ©ÔÄ¡£

°ì·¨7£º´¦Öóͷ£¶©ÔĻص÷

ÔÚ¶©ÔĻص÷ÖУ¬ÎÒÃÇÐèÒª¸üÐÂÓû§¶©ÔĵÄÄ¿½ñ״̬¡£ÈôÊǶ©Ôı»×÷·Ï»òÆÚÂú£¬ÎÒÃÇÐèҪƾ֤ÐèÒª×÷·Ï»ò»Ö¸´Óû§¶©ÔÄ¡£

ÔÚSubscriptionControllerÖÐÌí¼ÓÒÔÏÂÒªÁ죺

public function store(Request $request, SubscriberRepository $subscriberRepository)
{
    $paymentMethod = $request->input('payment_method');
    $plan = $request->input('plan');
    $subscriberRepository->subscribe(
        $request->user(),
        $plan,
        $paymentMethod
    );
    return response()->json([
        'success_url' => route('subscribe.invoice'),
    ]);
}
public function cancel(Request $request)
{
    $request->user()->subscription('main')->cancel();
    return redirect()->route('subscribe.index')
        ->with('success', 'Your subscription has been cancelled.');
}
public function resume(Request $request)
{
    $request->user()->subscription('main')->resume();
    return redirect()->route('subscribe.index')
        ->with('success', 'Your subscription has been resumed.');
}
public function invoice(Request $request)
{
    $invoice = $request->user()->invoices()->latest()->first();
    return view('subscription.invoice', [
        'invoice' => $invoice,
    ]);
}

µÇ¼ºó¸´ÖÆ

ÔÚÉÏÃæµÄ´úÂëÖУ¬ÎÒÃÇ´¦Öóͷ£Á˽¨ÉèеĶ©ÔÄ£¬×÷·Ï/»Ö¸´Óû§¶©ÔÄ£¬²¢ÏÔʾ¶©ÔĵÄ×îз¢Æ±¡£

ÏÖÔÚ£¬ÎÒÃÇÒѾ­ÀֳɵØʹÓÃLaravel CashierʵÏÖÁ˶©ÔĹ¦Ð§£¬²¢¿ÉÒÔÈÃÓû§½¨ÉèºÍÖÎÀíÆ䶩ÔÄÁË£¡

ÒÔÉϾÍÊÇLaravel¿ª·¢£ºÔõÑùʹÓÃLaravel CashierʵÏÖ¶©ÔĹ¦Ð§£¿µÄÏêϸÄÚÈÝ£¬¸ü¶àÇë¹Ø×¢±¾ÍøÄÚÆäËüÏà¹ØÎÄÕ£¡

ÃâÔð˵Ã÷£ºÒÔÉÏչʾÄÚÈÝȪԴÓÚÏàÖúýÌå¡¢ÆóÒµ»ú¹¹¡¢ÍøÓÑÌṩ»òÍøÂçÍøÂçÕûÀí£¬°æȨÕùÒéÓë±¾Õ¾Î޹أ¬ÎÄÕÂÉæ¼°¿´·¨Óë¿´·¨²»´ú±í×ðÁú¿­Ê±ÂËÓÍ»úÍø¹Ù·½Ì¬¶È£¬Çë¶ÁÕß½ö×ö²Î¿¼¡£±¾ÎĽӴýתÔØ£¬×ªÔØÇë˵Ã÷À´ÓÉ¡£ÈôÄúÒÔΪ±¾ÎÄÇÖÕ¼ÁËÄúµÄ°æȨÐÅÏ¢£¬»òÄú·¢Ã÷¸ÃÄÚÈÝÓÐÈκÎÉæ¼°ÓÐÎ¥¹«µÂ¡¢Ã°·¸Ö´·¨µÈÎ¥·¨ÐÅÏ¢£¬ÇëÄúÁ¬Ã¦ÁªÏµ×ðÁú¿­Ê±ÊµÊ±ÐÞÕý»òɾ³ý¡£

Ïà¹ØÐÂÎÅ

ÁªÏµ×ðÁú¿­Ê±

18523999891

¿É΢ÐÅÔÚÏß×Éѯ

ÊÂÇéʱ¼ä£ºÖÜÒ»ÖÁÖÜÎ壬9:30-18:30£¬½ÚãåÈÕÐÝÏ¢

QR code
¡¾ÍøÕ¾µØͼ¡¿¡¾sitemap¡¿