API class
Javascript
P S P Integration
Widgets | Plugins
Use Cases | Tutorials
PHP Example - vShop | Stripe

*** This is for PHP web-developers only ***

The below is an example of a PHP backend controller (KassshPaymentController) with a method called stripePayment() which is the Webhook to push a payment to the Merchant's PSP Stripe account. A lot of this code is very specific to the actual Shop platform (here vShop).

The important flow is

  • 1. The URL (Post) receives a Shop Order No (Cart No. if you like) + the kasssh token or barcode
  • 2. Using the kasssh.com API https://api.kasssh.com/api/charge/get it gets a full data-object for the kasssh token
  • 3. Optionally reconsile this with the internal order, i.e. do they match or are there any issues. There shouldn't be you might like to double check
  • 4. Retrieve your Stripe secret-key. This is entirely SHOP platform specific
  • 5. As this is Stripe we confirm or register our Key with them and here this is using Stripe's official PHP library which is installed via Composer as require stripe/stripe-php
  • 6a. The kasssh VCN (card number + details) is created with Stripe as a PaymentMethod, i.e. we tell Stripe to use this card for payment
  • 6b. Using Stripe's PaymentIntent with confirm (true) we push the payment to the PSP, i.e. debiting the card and the funds will now be in the Merchant's Stripe account
  • 7. Specific to the SHOP i.e. Business as usual and do whatever you `normally` do when a customer pays. In this example vShop creates a receipt entry, a consignment for shipping and confirms by eMail that all is well. None of this is related to kasssh and SHOP specific.

	
<?php

namespace App\Http\Controllers;

use Illuminate\Database\QueryException;
use Illuminate\Support\Collection;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

use GuzzleHttp\Exception\RequestException;

use DB;
use Log;
use Mail;

class KassshPaymentController extends Controller
{

	//
	//	vShop webhook for a kasssh.com (Stripe) payment
	// 	Demo is https://vshop.vadoo.co.uk/api/payments/kasssh/receive-to-stripe
	//

	// ---------------------------------------------------------------------------------------	

	public function stripePayment( Request $request ) {	

		//	--- G E N E R I C ---
		//
		//
		// 1. Receive Cart No + Kasssh Token or barcode
		// This really should be the Token, but we will allow a barcode as well (depends on Paypoint integration)
		//

		$payment = new \stdClass();
		$payment->kassshToken = $request->input('token');
		$payment->barcode = $request->input('barcode');
		$payment->cartID = $request->input('order_reference');

		$payment->PSP = 'STRIPE';

		//	--- G E N E R I C ---
		//
		//
		// 2. Get the kasssh.com token record using its API
		//

		// Call API...with httpPost()

		$data = array( 
			'token' => $payment->kassshToken, 
			'barcode' => $payment->barcode
		);
		$response = $this->httpPost( 'https://api.kasssh.com/api/charge/get', $data, null );

		// 
		// What we want here are the V C N details, i.e. card_number, cvv etc for a Stripe payment
		//

		if ( !empty($response) ) {

			// Convert API JSON reponse to an associative array
			$token = json_decode($response, true);
			$payment->card_number 			= $token['data']['vcn_card_number'];
			$payment->card_expiry_month		= $token['data']['vcn_expiry_month'];
			$payment->card_expiry_year		= $token['data']['vcn_expiry_year'];
			$payment->card_cvv			= $token['data']['vcn_cvv'];
			$payment->amount			= $token['data']['vcn_credit']/100;	/* Note in token is in pence | cents */

			$payment->email 			= $token['data']['shopper_email'];
			$payment->currency 			= 'GBP';	/* M V P */

		} else {

			$payment->message = 'Payment failed. Invalid kasssh.com token';
			$payment->response = -1;
			return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');		
		}


		//	--- V S H O P ---
		//
		// 	3. Specific to vShop HERE - get the Cart or Order and reconsile with token data
		//	-- This is entirely optional but good practice --
		//	i.e. Is the kasssh token data = Shop data 
		//


		$cart = DB::table('orders')
			->join('customers', 'customers.id', '=', 'orders.customer_id')
			->where('orders.id', '=', $payment->cartID)
			->select('orders.*', 'customers.email as customer_email')
			->get();

		// --- Reconsile.... ---


		//	--- V S H O P ---
		//
		// 4. What is the Stripe [Secret] API Key currently in play ?
		// -- This is vShop specific --
		//

		$stripeLive = DB::table('settings')
		    ->where('name','STRIPE-LIVE')
		    ->get();

		if ( $stripeLive->isEmpty() ) {
			// Bad settings file ?
			$payment->message  = 'Internal Error - No Live Stripe Flag';
			$payment->response = -1;
			return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');
		} else {
			if ( $stripeLive[0]->value ) {		/* Live */

				$stripeSecretKey = DB::table('settings')
				    ->where('name','STRIPE-SECRET-LIVE')
				    ->get();

				if ( $stripeSecretKey->isEmpty() ) {
					// Bad settings file ?
					$payment->message  = 'Internal Error - No Live Stripe Key';
					$payment->response = -2;
					return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');
				} else {
					$payment->stripeSecretKey = $stripeSecretKey[0]->value;
					$payment->live = 1;
				}

			} else {		/* Test */

				$stripeSecretKey = DB::table('settings')
				    ->where('name','STRIPE-SECRET-TEST')
				    ->get();

				if ( $stripeSecretKey->isEmpty() ) {
					// Bad settings file ?
					$payment->response = -3;
					$payment->message  = 'Internal Error - No Test Stripe Key';
					return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');
				} else {
					$payment->stripeSecretKey = $stripeSecretKey[0]->value;
					$payment->live = 0;
				}

			}
		}

		// 5. Establish/Register this API Key with the Stripe Controller
		try {
			// composer require stripe/stripe-php
			\Stripe\Stripe::setApiKey($payment->stripeSecretKey);

		} catch(\Stripe\Error\Base $e ) {						// Note Base Controller class
			// Stripe Error !
			$payment->message  = 'Internal Error - Invalid Stripe Key';
			$payment->response = -4;
			return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');
		}

		// 6. Create Payment Method with Stripe, E A S Y ....

		$paymentMethod = \Stripe\PaymentMethod::create([
			'type' => 'card',
			'card' => [
				'number' 	=> $payment->card_number,
				'exp_month'	=> $payment->card_expiry_month,
				'exp_year'	=> $payment->card_expiry_year,
				'cvc'		=> $payment->card_cvv
			],
		]);

		//
		// ??? --- Stripe API --- ??? Seems to return some odd string prior to the normal { opening JSON bracket
		// --  clean this ---
		//

		$paymentMethod = strstr($paymentMethod, '{');


		if ( !empty($paymentMethod) ) {

			// Convert API JSON reponse to an associative array to retrieve $paymentReply['id']
			$paymentReply = json_decode($paymentMethod, true);

			$charge = \Stripe\PaymentIntent::create([

				'amount'			=> (int) ($payment->amount*100),	// Convert to Integer e.g. 99.99 => 9999
				'currency'			=> $payment->currency,			// GBP or USD etc....
				'payment_method_types'		=> ['card'],
				'payment_method'		=> $paymentReply['id'],
				'confirm'			=> true,							// Automatic Debit YES
				//
				// 	H A R D   C O D E ....
				//
				'description' 			=> 'Payment for Cart/Order '.$payment->cartID

			]);

			// See above - why does Stripe return before the { ?
			$charge = strstr($charge, '{');				// Strip before first {
			// Convert API JSON reponse to an associative array to retrieve $chargeReply['id']
			$chargeReply = json_decode($charge, true);

	        $payment->tokenID = $chargeReply['id'];

		} else {
			// Stripe Error !
			$payment->message  = 'Internal Error - Stripe Failed with V C N';
			$payment->response = -5;
			return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');			
		}

		//
		//	The below is the normal flow for a vShop Payment
		//	B A U = Business as usual
		//	Any other shop would do want it normally does....
		//


		// 7. Create Receipt Entry [Generic]
		$payment->response = app('App\Http\Controllers\PaymentController')->createReceipt( $payment );

		// 8. Create Consignment Entry [Generic]
		$payment->response = app('App\Http\Controllers\PaymentController')->createConsignment( $payment );

		// 9. Create eMail [Generic]
		$payment->email = app('App\Http\Controllers\PaymentController')->createEmailReceipt( $payment );

		$payment->message = 'Payment was successful';
		$payment->response = 1;


		return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');

	}

	
	//
	//	The below is used when the customer/shopper agrees to use kasssh.com as a service
	//	It is NOT a payment as such = just a promise or commitment to pay.
	//	However, we consider this to be an undertaking from the customer and as such mark
	//	this order (cart) as Payment Promised. It's a bit `in limbo` but just `real (cash) payment pending` really
	//

	// ---------------------------------------------------------------------------------------	
	// Just update the order Status (status_id) to #PROMISEPAY
	//

	public function kassshPromisePayment( Request $request ) {	

		$payment = new \stdClass();

		$payment->PSP = 'KASSSH';
		$payment->cartID = $request->input('cartID');

		// Get the payment status_id for a #PROMISE, here kasssh.com, but could be anyone...
		$promisePayment = DB::table('order-status')
			->where('key_name', '=', '#PROMISEPAY')
			->get();

		if ( $promisePayment->isNotEmpty() ) {

			// Simply mark this Order/Cart as `promised to pay`
			DB::table('orders')
				->where('id', '=', $request->input('cartID') )
				->update(
				[
					'status_id' => $promisePayment[0]->id,
					'updated_at' => date("Y-m-d H:i:s")
				]
			);

			$payment->message = 'Payment has been promised';
			$payment->response = 1;

		} else {
			// Option not installed
			$payment->message = 'Payment option #PROMISEPAY not installed';
			$payment->response = -1;
		}

		return response()->json($payment, 200)->header('Access-Control-Allow-Origin', '*');      

	}

	//
	// Copied from kasssh.com API reference - of no real relevance here
	//

	public function httpPost( $url, $data, $header ) {
		$options = array(
		       'http' => array(
		     		'header'  => "Content-type: application/json\r\n".$header,
		        	'method'  => 'POST',
		        	'content' => json_encode($data)
		       ) );
		$context  = stream_context_create($options);
		return file_get_contents($url, false, $context);
	}

}