Flexible Payments with Stripe – Installation – Getting a token



Flexible Payments with Stripe – Installation – Getting a token

0 0


talk-confoo-2014-flexible-payments-with-stripe

Flexible payments with Stripe: talk for ConFoo 2014

On Github mkpeacock / talk-confoo-2014-flexible-payments-with-stripe

Flexible Payments with Stripe

Michael Peacock

@michaelpeacock

  • PHP Developer
  • Technical author
  • Occasional conference speaker
The online payment arena as been changing rapidly over the past year or two. Before, we were stuck with clunky, complex, unfriendly systems such as PayPal, WorldPay and the likes. Integrating them was difficult. Matching their payment options to your business case was often impossible, unless you were willing to pay additional monthly fees for this and that, as well as extra transaction fees. Recently, disruptive providers have came onto the scene to challenge that including stripe.

Minimum workflow

  • Customer provides card details
  • Stripe returns a token
  • Use the token to charge a card or save a customer
Stripe is disruptive because it gives you total control over payment strategy, the payment page, and the user experience, while removing you of all PCI-DSS compliance issues. Javascript is used to send the details direct to stripe - all you ever see is a token. FRAGMENTS: customer provides card, stripe returns token, use token to charge card, or save customer.

Costs

Pretty good value transaction fees. No need for a merchant bank account. So the stripe fees are it. If you charge in different currencies, there is a conversion fee, if you don't have an account in that currency.

Payment strategies

  • One-off payments
  • On-demand payments
  • Subscription payments
  • Metered billing
  • Seat/quantity based pricing
Fragments: one off; on-demand; subscription; metered; seat/quantity based

Other strategies

  • Stripe Connect
  • Transfers
- Stripe connect lets you send payments directly to other stripe users, you can charge a transaction fee on this too. Useful for online market places. - Transfers let you send money to a specific bank account (US stripe customers only for now :-()

Installation

To install; we need the PHP component, the Javascript library, and we need to set our API key

Composer.json

"stripe/stripe-php": "v1.11.0",

Javascript

<script type="text/javascript" src="https://js.stripe.com/v2/">
'stripe': '//js.stripe.com/v2/?1',

Set the API key

\Stripe::setApiKey($api_secret);

Getting a token

There are two ways to get a stripe token. We can use Stripe checkout, for a stripe provided, payment collection page; or we can use StripeJS for our own hosted payment collection-page.

Stripe Checkout

<form action="/charge" method="POST">
  <script
    src="https://checkout.stripe.com/checkout.js" class="stripe-button"
    data-key="{{ stripe_publishable_key }}"
    data-image="/square-logo.png"
    data-currency="CAD"
    data-name="Confoo"
    data-description="1 ticket ($845.00)"
    data-amount="84500">
  </script>
</form>
By including the stripe checkout javascript, this places a button on your site. Clicking that button presents the user with a stripe-provided modal window, which collects payment details. The data attributes let you customise the details of the charge, and even add a logo to the modal.

Stripe.js

Stripe.card.createToken({
    number: $('.card-number').val(),
    cvc: $('.card-cvc').val(),
    exp_month: $('.card-expiry-month').val(),
    exp_year: $('.card-expiry-year').val()
}, stripeResponseHandler);
Stripe JS can be used to create a token from a self-hosted form, provided you disable the default form action with JavaScript. Two caveats; SSL is mandatory for production; and the form fields which relate to the credit card data for stripe cannot have a name attribute (this prevents them ever hitting your server, so you can avoid PCI-DSS woes)

ResponseHandler

function stripeResponseHandler(status, response) {
    if (response.error) {
        // show the errors on the form
        $(".payment-errors").text(response.error.message);
    } else {
        var form$ = $("#payment-form");
        // token contains id, last4, and card type
        var token = response['id'];
        // insert the token into the form so it gets submitted to the server
        form$.append("");
        // and submit
        form$.get(0).submit();
    }
}
A response handler function is needed to deal with errors and present them to the user, and on success to insert the token to the form, and submit the form.

One-off payment

try {
    $charge = \Stripe_Charge::create(
        [
            'amount' =>  84500 // amount in cents
            'currency' => 'CAD',
            'card' => $_POST['stripeToken'],
            'description' => 'One off payment'
        ]
    );
} catch (\Exception $e) {
    // error processing payment
}

Exceptions to look out for

\Stripe_CardError
\Stripe_ApiConnectionError
\Stripe_InvalidRequestError
\Stripe_ApiError

On-demand payments

Three steps: create a customer in stripe; associate the stripe customer ID with the customer record in your own system; charge the customer on demand.

Create a customer

$stripe_customer = \Stripe_Customer::create([
	"card" => $_POST['stripeToken'],
	"email" => 'customer@domain.com'
]);

Associate the id

$user->setStripeCustomerId($stripe_customer->id);

Charge the customer

$charge = \Stripe_Charge::create([
	'customer' => $user->getStripeCustomerId(),
	'amount'   => 84500,
	'currency' => 'cad'
]);

Subscription payments

Create a plan

\Stripe_Plan::create([
	"amount" => 1000,
	"interval" => "month",
	"name" => "Friend of Confoo",
	"currency" => "cad",
	"id" => "friend"
]);

Subscribe a customer to the plan

$customer = \Stripe_Customer::create([
	"card" => $_POST['stripeToken'],
	"plan" => "friend",
	"email" => "customer@mydomain.com"
]);

Change plans

$customer = \Stripe_Customer::retrieve($stripe_customer_id);

$customer->updateSubscription([
	'plan' => 'proplan',
	'prorate' => true
]);

Trial periods

\Stripe_Plan::create([
	"amount" => 1000,
	"interval" => "month",
	"name" => "Friend of Confoo - with trial",
	"currency" => "cad",
	"id" => "friendtrial",
	"trial_period_days" => 30
]);

Discounts

$coupon = \Stripe_Coupon::create([
	"percent_off" => 10,
	"duration" => "duration",
	"max_redemptions" => 10
	"currency" => "cad",
]);

$customer = \Stripe_Customer::retrieve($stripe_customer_id);
$customer->coupon = $coupon->id;
$customer->save();

Metered billing

$additional_cost = \Stripe_InvoiceItem::create([
	"customer" => $user->getStripeCustomerId(),
	"amount" => 500,
	"currency" => "cad",
	"description" => "Ad-hoc services for this month"
]);

Seat / Quantity based pricing

\Stripe_Plan::create([
	"amount" => 500,
	"interval" => "month",
	"name" => "Per user pricing plan",
	"currency" => "cad",
	"id" => "ppu"
]);

Subscribe a customer to the plan

$customer = \Stripe_Customer::create([
	"card" => $_POST['stripeToken'],
	"plan" => "ppu",
	"quantity" => 5,
	"email" => "customer@mydomain.com"
]);
$customer = \Stripe_Customer::retrieve($stripe_customer_id);
$plan = $customer->subscription->plan->id;
$new_quantity = $customer->subscription->quantity + 1;

$customer->updateSubscription([
	'quantity' => $new_quantity
]);

Stripe Connect

<a href="https://connect.stripe.com/oauth/authorize?response_type=code&client_id={{ stripe_connect_client_id }}">Connect with Stripe</a>
$post_data = [
    'client_id' => urlencode($stripe_connect_client_id),
	'client_secret' => urlencode($stripe_connect_client_secret),
	'code' => urlencode($_GET['code']),
	'grant_type' => urlencode('authorization_code')
];
$post_fields = "";
foreach($post_data as $key => $value) {
	$post_fields .= $key . '=' . $value . '&';
}
rtrim($post_fields, '&');

$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, 'https://connect.stripe.com/oauth/token');
curl_setopt($ch,CURLOPT_POST, count($post_data));
curl_setopt($ch,CURLOPT_POSTFIELDS, $post_fields);
$result = curl_exec($ch);
curl_close($ch);
						
$stripe_data = json_decode($result, true);
$customer_publishable_key = $stripe_data['stripe_publishable_key'];
$customer_stripe_user_id = $stripe_data['stripe_user_id'];
$customer_stripe_refresh_token = $stripe_data['refresh_token'];
$customer_stripe_access_token = $stripe_data['access_token']

Charges

  • Use your customers publishable key
  • Use your customers access token in place of the API key
$charge = \Stripe_Charge::create([
	"amount" => 1000,
	"currency" => "cad",
	"card" => $token,
	"description" => "Purchase",
],
 $customer_stripe_access_token // users stripe access token in place of API key
);

Shared customers

  • Use your publishable key
  • Use your customers access token in place of the API key

Get a token for a shared customer to charge

$token = \Stripe_Token::create(
  ["customer" => $customer_id],
  $customer_stripe_access_token // users stripe access token in place of API key
);

Charge the token

$charge = Stripe_Charge::create([
	"amount" => 1000,
	"currency" => "cad",
	"card" => $token->id,
	"description" => "Payment"]
);

Transaction fees

"application_fee" => 100
"application_fee_percent" => 10

Transfers

Stripe.bankAccount.createToken({
    country: $('.country').val(),
    routingNumber: $('.routing-number').val(),
    accountNumber: $('.account-number').val(),
}, stripeResponseHandler);
$recipient = \Stripe_Recipient::create([
		"name" => "Michael Peacock",
		"type" => "individual",
		"bank_account" => $token,
		"email" => "me@domain.com"
	]);
$transfer = Stripe_Transfer::create([
		"amount" => 1000,
		"currency" => "cad",
		"recipient" => $recipient->id,
		"statement_descriptor" => "Commission for referrals"
	]);

Testing

  • Use test publishable key
  • Use test secret
  • Use test cards
  • SSL optional
  • Use test stripe connect application details

Going live

  • Use live publishable key
  • Use live secret
  • Use live cards
  • SSL mandatory
  • Use live stripe connect application details

Thanks!

@michaelpeacockwww.michaelpeacock.co.uk

http://mkpeacock.github.io/talk-confoo-2014-flexible-payments-with-stripe