Obsługa płatności za pomocą Stripe w php

W artykule pokażę jak obsłużyć płatności online za pomocą Stripe. Najpierw należy zarejestrować się na powyższej stronie. Po zalogowaniu na stronie dashboard.stripe.com będziemy mogli podglądać testowe płatności. Ponadto klikając na " Get your test API keys" zobaczymy nasze developerskie klucze api, które będą nam potrzebne.

Na początku w katalogu projektu wykonajmy poniższe polecenie composera.
composer require stripe/stripe-php
Pobierze nam ono bibliotekę do obsługi płatności Stripe w php. Zanim przejdziemy do właściwego kodu projektu wykonajmy testowy request do api. Utwórz zatem dowolny plik php z następującą treścią.
	require_once('vendor/autoload.php');
	\Stripe\Stripe::setApiKey('twoj_secret_key');

	$intent = \Stripe\PaymentIntent::create([
		'amount' => 25.00*100,
		'currency' => 'gbp',
		'payment_method_types' => ['card'],
		'receipt_email' => '[email protected]',
	]);
	echo '<pre>';
	print_r($intent);
Stripe powinien zwrócić obiekt PaymentIntent w odpowiedzi na żądanie API (spójrz na poniższy listing).
{
  "id": "pi_1DRuHnHgsMRlo4MtwuIAUe6u",
  "object": "payment_intent",
  "amount": 1000,
  "amount_capturable": 0,
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/charges?payment_intent=pi_1DRuHnHgsMRlo4MtwuIAUe6u"
  },
  "client_secret": "{{PAYMENT_INTENT_CLIENT_SECRET}}",
  "confirmation_method": "automatic",
  "created": 1556123069,
  "currency": "usd",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {},
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": null,
  "payment_method_types": [
    "card"
  ],
  "receipt_email": "[email protected]",
  "review": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "status": "requires_payment_method",
  "transfer_data": null,
  "transfer_group": null
}
Teraz przejdziemy do właściwego projektu. Utwórzmy plik start.html z następującą zawartością.
<html>
<head>
    <!-- The Styling File -->
    <link rel="stylesheet" href="./style.css"/>
</head>
<body>

    <div class="form-row">
        <label for="card-element">Credit or debit card</label>
        <input id="cardholder-name" type="text">
        <!-- placeholder for Elements -->
        <div id="card-element"></div>
        <button id="card-button">Submit Payment</button>
        <!-- Used to display form errors -->
        <div id="card-errors"></div>
    </div>
<!-- The needed JS files -->
<!-- JQUERY File -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- Stripe JS -->
<script src="https://js.stripe.com/v3/"></script>
<!-- Your JS File -->
<script src="./charge2.js"></script>
</body>
</html>
Jak widać zaciągamy w nim style, jquery oraz javascriptową bibliotekę stripe. Ponadto w kodzie umieszczamy kilka divów, w których wyświetlane będą odpowiednie informacje np. #card-element i #card-errors. Ponadto mamy link do pliku js charge2.js, którego treść poniżej.
var stripe = Stripe('twoj_public_key');

var elements = stripe.elements();
// Set up Stripe.js and Elements to use in checkout form
var style = {
    base: {
        color: '#32325d',
        lineHeight: '24px',
        fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
        fontSmoothing: 'antialiased',
        fontSize: '16px',
        '::placeholder': {
            color: '#aab7c4'
        }
    },
    invalid: {
        color: '#fa755a',
        iconColor: '#fa755a'
    }
};

var cardElement = elements.create('card', {style: style, hidePostalCode: true});
cardElement.mount('#card-element');

var cardholderName = document.getElementById('cardholder-name');
var cardButton = document.getElementById('card-button');

cardButton.addEventListener('click', function(ev) {
    stripe.createPaymentMethod('card', cardElement, {
        billing_details: {name: cardholderName.value}
    }).then(function(result) {
        if (result.error) {
            // Show error in payment form
        } else {
            // Otherwise send paymentMethod.id to your server (see Step 3)
            fetch('server.php', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ payment_method_id: result.paymentMethod.id })
            }).then(function(result) {
                // Handle server response (see Step 3)
                result.json().then(function(json) {
                    handleServerResponse(json);
                })
            });
        }
    });
});

function handleServerResponse(response) {
    if (response.error) {
        // Show error from server on payment form
    } else if (response.requires_action) {
        // Use Stripe.js to handle required card action
        stripe.handleCardAction(
            response.payment_intent_client_secret
        ).then(function(result) {
            if (result.error) {
                // Show error in payment form
            } else {
                // The card action has been handled
                // The PaymentIntent can be confirmed again on the server
                fetch('server.php', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ payment_intent_id: result.paymentIntent.id })
                }).then(function(confirmResult) {
                    return confirmResult.json();
                }).then(handleServerResponse);
            }
        });
    } else {
        // Show success message
        console.log('Success');
    }
}
Korzystamy tu z instancji obiektu Stripe, któremu przekazujemy nasz klucz publiczny api. Następnie podczepiamy się pod element card-element. Będą się tam wyświetlać pola na wpisanie nr karty itd. W funkcji handleServerResponse potwierdzamy płatność łącząc się do pliku server.php, gdzie wykonujemy kod sprawdzający po stronie serwera.
require_once('vendor/autoload.php');

\Stripe\Stripe::setApiKey('twoj_secret_key');

header('Content-Type: application/json');

# retrieve json from POST body
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);

$intent = null;
try {
    if (isset($json_obj->payment_method_id)) {
        # Create the PaymentIntent
        $intent = \Stripe\PaymentIntent::create([
            'payment_method' => $json_obj->payment_method_id,
            'amount' => 1095,
            'currency' => 'gbp',
            'confirmation_method' => 'manual',
            'confirm' => true,
        ]);
    }
    if (isset($json_obj->payment_intent_id)) {
        $intent = \Stripe\PaymentIntent::retrieve(
            $json_obj->payment_intent_id
        );
        $intent->confirm();
    }
    generateResponse($intent);
} catch (\Stripe\Exception\ApiErrorException $e) {
    # Display error on client
    echo json_encode([
        'error' => $e->getMessage()
    ]);
}

function generateResponse($intent) {
    # Note that if your API version is before 2019-02-11, 'requires_action'
    # appears as 'requires_source_action'.
    if ($intent->status == 'requires_action' &&
        $intent->next_action->type == 'use_stripe_sdk') {
        # Tell the client to handle the action
        echo json_encode([
            'requires_action' => true,
            'payment_intent_client_secret' => $intent->client_secret
        ]);
    } else if ($intent->status == 'succeeded') {
        # The payment didn’t need any additional actions and completed!
        # Handle post-payment fulfillment
        echo json_encode([
            "success" => true
        ]);
    } else {
        # Invalid status
        http_response_code(500);
        echo json_encode(['error' => 'Invalid PaymentIntent status']);
    }
}
Ważną informacją jest to, że kwoty w Stripe podawane są jako liczby typu int. Dlatego aby pobrać np. kwotę 10.95 GBP jako amount musimy podać 1095. Poniżej zamieszczam również kod pliku style.css
.StripeElement {
    background-color: white;
    padding: 8px 12px;
    border-radius: 4px;
    border: 1px solid transparent;
    box-shadow: 0 1px 3px 0 #e6ebf1;
    -webkit-transition: box-shadow 150ms ease;
    transition: box-shadow 150ms ease;
}
.StripeElement--focus {
    box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
    border-color: #fa755a;
}
.StripeElement--webkit-autofill {
    background-color: #fefde5 !important;
}