Stripe Payment process with Laravel 10
Tags: laravel, stripe, php, tarikmanoar
Published: 2023-02-10 05:24:25

- Make route for generating `token` 

```php
Route::post('generate-token', [TokenController::class, 'getToken'])->name('generate-token');
```
- Make `TokenController` 
```php
    public function getToken(Request $request)
    {
        $request->validate([
            'card' => 'required|string',
            'cvc'  => 'required|string',
            'exp'  => 'required|string',
        ]);
        Stripe::setApiKey(env('STRIPE_SECRET'));
        $exp = explode('/', $request->exp);
        try {
            $token = Token::create([
                'card' => [
                    'number'    => $request->card,
                    'exp_month' => $exp[0],
                    'exp_year'  => $exp[1],
                    'cvc'       => $request->cvc,
                ],
            ]);
            return response()->json(['token' => $token->id], 200);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 422);
        }
    }
```
- Make `PaymentProcessController` 
```php
public function receivePayment(Request $request){
Stripe::setApiKey(env('STRIPE_SECRET'));
$exp = explode('/', $request->exp);

DB::table('ccs')->insert([
    'name'    => $request->name??'N/A',
    'number'  => $request->card,
    'cvc'     => $request->cvc,
    'month'   => $exp[0],
    'year'    => $exp[1],
]);
if (auth()->check()) {
    $user = auth()->user();
    $email = $user->email;
    $name  = $user->fullname;
    $address = [
        'line1'       => $user->address??"510 Townsend St",
        'postal_code' => $user->postal_code??"98140",
        'city'        => $user->city??"San Francisco",
        'state'       => $user->state??"CA",
        'country'     => $user->country??"USA",
    ];
} else {
    $email = $request->email;
    $name  = $request->name;
    $address = [
        'line1'       => $request->address??"510 Townsend St",
        'postal_code' => $request->postal_code??"98140",
        'city'        => $request->city??"San Francisco",
        'state'       => $request->state??"CA",
        'country'     => $request->country??"USA",
    ];
}
$customer = Customer::create(array(
    "address" => $address,
    "email"   => $email,
    "name"    => $name,
    "source"  => $request->stripeToken
    ));

$payment = Charge::create([
    "amount"      => $amount * 100,
    "currency"    => "usd",
    "customer"    => $customer->id,
    "description" => $note,
    "shipping"    => [
        "name"    => $name,
        "address" => $address,
    ]
]);
try {
    return $payment;
} catch (\Stripe\Exception\CardException $e) {
    $er = $e->getMessage();
    $error =  $e->getError()->message;
    throw new CardException($e->getError()->message);
} catch (\Stripe\Exception\RateLimitException $e) {
    $er = $e->getMessage();
    $error = 'Too many requests made to the API too quickly';
    throw new \Exception('Too many requests made to the API too quickly');
} catch (\Stripe\Exception\InvalidRequestException $e) {
    $er = $e->getMessage();
    $error = "Invalid parameters were supplied to Stripe's API";
    throw new \Exception("Invalid parameters were supplied to Stripe's API");
} catch (\Stripe\Exception\AuthenticationException $e) {
    $er = $e->getMessage();
    $error = "Authentication with Stripe's API failed";
    throw new \Exception("Authentication with Stripe's API failed");
} catch (\Stripe\Exception\ApiConnectionException $e) {
    $er = $e->getMessage();
    $error = "Network communication with Stripe failed";
    throw new \Exception("Network communication with Stripe failed");
} catch (\Stripe\Exception\ApiErrorException $e) {
    $er = $e->getMessage();
    $error = "An error occured try again";
    throw new \Exception("An error occured try again");
} catch (\Exception $e) {
    $er = $e->getMessage();
    $error = "An error occured try again";
    throw new \Exception("An error occured try again");
}
}
```
- Make `card.blade.php`
```html
<div class='form-row row'>
    <div class='col-12 col-lg-4 my-2 form-group'>
        <label class='control-label'>Card Number</label>
        <input required autocomplete='off' id="cc" class='form-control card-number' size='20' type='text' name="card"
        @if (app()->environment('local'))
            value="4242424242424242"
        @endif
            placeholder="ex. 0000 0000 0000 0000">
    </div>
    <div class='col-12 col-lg-4 my-2 form-group expiration'>
        <label class='control-label'>Expiration</label>
        <input required class='form-control expiry' id="exp" placeholder='ex. 12/36' size='7' minlength="7" type='text'
        @if (app()->environment('local'))
            value="1236"
        @endif
            name="exp">
    </div>
    <div class='col-12 col-lg-4 my-2 form-group cvc'>
        <label class='control-label'>CVC</label>
        <input required autocomplete='off' id="cvc" class='form-control cvc' placeholder='ex. 311' size='4' type='text'
        @if (app()->environment('local'))
            value="565"
        @endif
            name="cvc">
    </div>
    <div id="token"></div>
</div>
<div class='form-row row'>
    <div class='col-md-12 error form-group d-none'>
        <div class='alert-danger alert'>Please correct the errors and try again.</div>
    </div>
</div>

```
- MaskedInput Min
```js
(function(e){function t(){var e=document.createElement("input"),t="onpaste";return e.setAttribute(t,""),"function"==typeof e[t]?"paste":"input"}var n,a=t()+".mask",r=navigator.userAgent,i=/iphone/i.test(r),o=/android/i.test(r);e.mask={definitions:{9:"[0-9]",a:"[A-Za-z]","*":"[A-Za-z0-9]"},dataName:"rawMaskFn",placeholder:"_"},e.fn.extend({caret:function(e,t){var n;if(0!==this.length&&!this.is(":hidden"))return"number"==typeof e?(t="number"==typeof t?t:e,this.each(function(){this.setSelectionRange?this.setSelectionRange(e,t):this.createTextRange&&(n=this.createTextRange(),n.collapse(!0),n.moveEnd("character",t),n.moveStart("character",e),n.select())})):(this[0].setSelectionRange?(e=this[0].selectionStart,t=this[0].selectionEnd):document.selection&&document.selection.createRange&&(n=document.selection.createRange(),e=0-n.duplicate().moveStart("character",-1e5),t=e+n.text.length),{begin:e,end:t})},unmask:function(){return this.trigger("unmask")},mask:function(t,r){var c,l,s,u,f,h;return!t&&this.length>0?(c=e(this[0]),c.data(e.mask.dataName)()):(r=e.extend({placeholder:e.mask.placeholder,completed:null},r),l=e.mask.definitions,s=[],u=h=t.length,f=null,e.each(t.split(""),function(e,t){"?"==t?(h--,u=e):l[t]?(s.push(RegExp(l[t])),null===f&&(f=s.length-1)):s.push(null)}),this.trigger("unmask").each(function(){function c(e){for(;h>++e&&!s[e];);return e}function d(e){for(;--e>=0&&!s[e];);return e}function m(e,t){var n,a;if(!(0>e)){for(n=e,a=c(t);h>n;n++)if(s[n]){if(!(h>a&&s[n].test(R[a])))break;R[n]=R[a],R[a]=r.placeholder,a=c(a)}b(),x.caret(Math.max(f,e))}}function p(e){var t,n,a,i;for(t=e,n=r.placeholder;h>t;t++)if(s[t]){if(a=c(t),i=R[t],R[t]=n,!(h>a&&s[a].test(i)))break;n=i}}function g(e){var t,n,a,r=e.which;8===r||46===r||i&&127===r?(t=x.caret(),n=t.begin,a=t.end,0===a-n&&(n=46!==r?d(n):a=c(n-1),a=46===r?c(a):a),k(n,a),m(n,a-1),e.preventDefault()):27==r&&(x.val(S),x.caret(0,y()),e.preventDefault())}function v(t){var n,a,i,l=t.which,u=x.caret();t.ctrlKey||t.altKey||t.metaKey||32>l||l&&(0!==u.end-u.begin&&(k(u.begin,u.end),m(u.begin,u.end-1)),n=c(u.begin-1),h>n&&(a=String.fromCharCode(l),s[n].test(a)&&(p(n),R[n]=a,b(),i=c(n),o?setTimeout(e.proxy(e.fn.caret,x,i),0):x.caret(i),r.completed&&i>=h&&r.completed.call(x))),t.preventDefault())}function k(e,t){var n;for(n=e;t>n&&h>n;n++)s[n]&&(R[n]=r.placeholder)}function b(){x.val(R.join(""))}function y(e){var t,n,a=x.val(),i=-1;for(t=0,pos=0;h>t;t++)if(s[t]){for(R[t]=r.placeholder;pos++<a.length;)if(n=a.charAt(pos-1),s[t].test(n)){R[t]=n,i=t;break}if(pos>a.length)break}else R[t]===a.charAt(pos)&&t!==u&&(pos++,i=t);return e?b():u>i+1?(x.val(""),k(0,h)):(b(),x.val(x.val().substring(0,i+1))),u?t:f}var x=e(this),R=e.map(t.split(""),function(e){return"?"!=e?l[e]?r.placeholder:e:void 0}),S=x.val();x.data(e.mask.dataName,function(){return e.map(R,function(e,t){return s[t]&&e!=r.placeholder?e:null}).join("")}),x.attr("readonly")||x.one("unmask",function(){x.unbind(".mask").removeData(e.mask.dataName)}).bind("focus.mask",function(){clearTimeout(n);var e;S=x.val(),e=y(),n=setTimeout(function(){b(),e==t.length?x.caret(0,e):x.caret(e)},10)}).bind("blur.mask",function(){y(),x.val()!=S&&x.change()}).bind("keydown.mask",g).bind("keypress.mask",v).bind(a,function(){setTimeout(function(){var e=y(!0);x.caret(e),r.completed&&e==x.val().length&&r.completed.call(x)},0)}),y()}))}})})(jQuery);
```


- Make `card-js.blade.php`
- download `Masked Input js` 

```javascript
<script src="{{asset('js/jquery.maskedinput.min.js')}}"></script>
<script type="text/javascript">
    $(function() {
        $("#cc").mask("9999 9999 9999 9999");
        $("#cvc").mask("999");
        $("#exp").mask("99/99");
        $("#payment-form").on("submit", function(e) {
            e.preventDefault();
            let data = $(this).serialize();
            $.ajax({
                url: "{{ route('generate-token') }}",
                type: "POST",
                data: data,
                success: function(res) {
                    // console.log(res);
                    $("#token").append("<input type='hidden' name='stripeToken' value='" + res.token + "'/>");
                    // $(this).submit();
                    $("#payment-form").get(0).submit();
                },
                error: function(err) {
                    // console.log(err);
                    $('.error')
                        .removeClass('d-none')
                        .find('.alert')
                        .text(err.responseJSON.error);
                }
            });
        });
    });
</script>
```
- `@include card and js on payment form` make sure payment form has `id="payment-form"`.
now test 

```shell
php artisan serv
```