Files
booking/booking.stripe.inc
2016-07-23 12:23:44 +10:00

322 lines
12 KiB
PHP

<?php
/**
* @file
* Functions for stripe payment integration
* @see https://github.com/ericthelast/drupal-stripe-form and https://www.webomelette.com/drupal-stripe-integration
*/
/**
* Get the current stripe api public key
*/
function _booking_get_stripe_public_key() {
if (variable_get('booking_stripe_testmode', 0) == 1) {
return variable_get('booking_stripe_test_public_key', '');
}
else {
return variable_get('booking_stripe_live_public_key', '');
}
}
/**
* Get the current stripe api private key
*/
function _booking_get_stripe_private_key() {
if (variable_get('booking_stripe_testmode', 0) == 1) {
return variable_get('booking_stripe_test_secret_key', '');
}
else {
return variable_get('booking_stripe_live_secret_key', '');
}
}
/**
* Helper function to calculate stripe transaction fees
*/
function _booking_add_stripe_fees($amount, $country) {
//add the 30 cent fixed cost
$result = '0.00';
$result = (float) ($amount + (float) variable_get('booking_stripe_transaction_fee_fixedcost', '0.3'));
//and the 2.6 percent transaction fee for australian transaction with no currency conversion
if ($country == variable_get('booking_default_country', 'Australia')) {
$percentage = (float) variable_get('booking_stripe_transaction_fee_percentage', '1.75');
} else {
$percentage = (float) variable_get('booking_stripe_transaction_fee_percentage_intl', '2.9');
}
//apply the percentage
$percentage = (float) $percentage / 100;
$result = $result / (1 - $percentage);
//return result
return $result;
}
/**
* Helper function to generate paypal form for payments
*/
function _booking_stripe_form($person, $invoiceid, $net_amount_owing, $button_text) {
$payment_form = drupal_get_form('booking_stripe_form', $person, $invoiceid, $net_amount_owing, $button_text);
return drupal_render($payment_form);
}
function booking_stripe_form($node, &$form_state, $person, $invoiceid, $net_amount_owing, $button_text) {
global $event;
$settings = array();
$form = array();
//set some values for our internal stripe library to help process the form
//these will be used by the attached js from booking-strip library to identify which parts of the form to process
$setting['booking_stripe'] = array(
'pubkey' => _booking_get_stripe_public_key(),
'form_selector' => str_replace('_', '-', __FUNCTION__),
'name' => $event->booking_eventname,
'image' => variable_get('booking_stripe_logo', ''),
'payment_button_label' => '{{amount}} (plus fees)',
);
//attach settings and javascript to the form
$form['#attached'] = array(
'js' => array(
array('data' => $setting, 'type' => 'setting'),
),
'library' => array(
array('booking', 'booking-stripe'),
),
);
//paypal specific settings
$vars = array(
'nid' => $person->nid,
'email' => $person->booking_email,
'description' => $event->booking_eventname . ' ' . $person->booking_price_descrip,
'invoice' => $invoiceid,
'amount' => $net_amount_owing,
'gross_amount' => number_format(_booking_add_stripe_fees($net_amount_owing, $person->booking_country), 2, '.', ''),
'foreign_gross_amount' => number_format(_booking_add_stripe_fees($net_amount_owing, 'FakeCountry'), 2, '.', ''),
'last_name' => $person->booking_lastname,
'first_name' => $person->booking_firstname,
'uuid' => $person->booking_tempid,
'token_id' => '',
'token_email' => '',
'token_client_ip' => '',
'card_brand' => '',
'card_country' => '',
'card_cvc_check' => '',
'card_address_zip_check' => '',
);
//turn the array into a form
foreach($vars as $name => $value) {
$form[$name] = array(
'#type' => 'hidden',
'#value' => $value,
);
};
$form['submit'] = array(
'#id' => 'stripe-submit',
'#type' => 'button',
'#value' => t($button_text),
);
$form['#after_build'][] = 'booking_stripe_add_final_validation';
//watchdog('booking', 'Booking Balance payment: @info', array ('@info' => var_export($form, TRUE)));
return $form;
}
/**
* Tries to add final validation after all else has been added through alters.
*/
function booking_stripe_add_final_validation($form) {
$form['#validate'][] = 'booking_stripe_validate_form_payment';
return $form;
}
/**
* Form validation callback.
*/
function booking_stripe_checkout_form_validate($form, &$form_state) {
// Validate normal form elements as needed.
}
/**
* Processes the stripe payment.
*
* We do this here so that if the payment fails,
* we're still in a validation stage and can return
* early. If success, we'll pass the charge on
* to the submission callback.
*/
function booking_stripe_validate_form_payment($form, &$form_state) {
global $event;
if($errors = form_get_errors()) {
//@todo log an error via watchdog
return;
}
$path = libraries_get_path('stripe');
require_once($path . '/init.php');
\Stripe\Stripe::setApiKey(_booking_get_stripe_private_key());
//get values from original form
//@todo sanitise this input
$token = (isset($form_state['input']['token_id']) ? $form_state['input']['token_id'] : '');
$amount = (isset($form_state['input']['gross_amount']) ? $form_state['input']['gross_amount'] : '');
$invoice = (isset($form_state['input']['invoice']) ? $form_state['input']['invoice'] : '');
$nid = (isset($form_state['input']['nid']) ? $form_state['input']['nid'] : '');
$tempid= (isset($form_state['input']['uuid']) ? $form_state['input']['uuid'] : '');
$last_name = (isset($form_state['input']['last_name']) ? $form_state['input']['last_name'] : '');
$first_name = (isset($form_state['input']['first_name']) ? $form_state['input']['first_name'] : '');
$card_brand = (isset($form_state['input']['card_brand']) ? $form_state['input']['card_brand'] : '');
$card_country = (isset($form_state['input']['card_country']) ? $form_state['input']['card_country'] : '');
//if card issuer is american express then the transaction fee charged should be the international rate
if ($card_brand === 'American Express') {
$person = node_load($nid);
$amount = (isset($form_state['input']['foreign_gross_amount']) ? $form_state['input']['foreign_gross_amount'] : $amount);
watchdog('booking_debug', "Detected Amex card use, setting amount to !amount", array('!amount' => $amount));
}
//if card is issued from a different country then apply the international rate also
//@todo figure out a better way of matching country name to country code
// probably via https://api.drupal.org/api/drupal/includes%21locale.inc/function/country_get_list/7.x
//NOTE! This will result in incorrect charge amounts when using the test API due to a stripe bug
//since the australian test card number gets charged 2.9% instead of 1.75%
elseif (variable_get('booking_default_country', 'Australia') === 'Australia' && $card_country !== 'AU') {
$person = node_load($nid);
$amount = (isset($form_state['input']['foreign_gross_amount']) ? $form_state['input']['foreign_gross_amount'] : $amount);
watchdog('booking_debug', "Detected foreign card use (country !country), setting amount to !amount",
array('!country' => $card_country, '!amount' => $amount));
}
// Create the charge on Stripe's servers - this will charge the user's card
try {
$charge = \Stripe\Charge::create(array(
"amount" => $amount * 100,
"currency" => "aud",
"card" => $token,
"statement_descriptor" => substr($event->booking_eventname, 0, 21), //this field is limited to 22 characters
"expand" => array('balance_transaction'),
"description" => $form_state['input']['description'],
"receipt_email" => $form_state['input']['email'],
"metadata" => array(
"invoice" => $invoice,
"nid" => $nid,
"last_name" => $last_name,
"first_name" => $first_name,
),
));
watchdog('booking_debug', "<pre>Stripe payment charge results:\n@info</pre>", array('@info' => print_r( $charge->__toJSON(), true)));
if ($charge && $charge->paid) {
//watchdog('booking_debug', 'Charge created successfully');
_booking_process_stripe_payment($charge, $token);
//$form_state['stripeform_charge'] = $charge;
// @todo call _booking_process_stripe_payment to store payment
drupal_goto('bookingfinal/' . $tempid);
}
else {
watchdog('booking', 'Charge was not created successfully');
drupal_set_message('Card does not seem to have been charged successfully. Please try again', 'error');
}
}
catch(\Stripe\Error\Card $e) {
$e_json = $e->getJsonBody();
$error = $e_json['error'];
watchdog('booking', $e->getMessage());
drupal_set_message($error['message'], 'error');
}
catch (\Stripe\Error\ApiConnection $e) {
watchdog('booking', $e->getMessage());
drupal_set_message($error['message'], 'error');
}
catch (\Stripe\Error\Api $e) {
watchdog('booking', $e->getMessage());
drupal_set_message($error['message'], 'error');
}
}
function _booking_process_stripe_payment(&$charge, $token) {
global $event;
$balance_payment = false;
$amount_owing = 0;
//$invoice = $data->metadata;
//verify the status of the charge
if (empty($charge->status) || ($charge->status != 'succeeded')) {
$successful = FALSE;
}
else {
$successful = TRUE;
}
//extract the person node id from the invoice
$pos = strpos($charge->metadata->invoice, "_");
if (($pos === false) || ($pos == 0)) {
watchdog('booking', 'Unable to process payment with invalid invoice information: !id', array('!id' => $charge->metadata->invoice), WATCHDOG_ERROR);
return;
}
//get the part of the invoice up to the first underscore
$nid = substr($charge->metadata->invoice, 0, $pos);
//get the data between the first and second underscore
$eid = substr($charge->metadata->invoice, $pos + 1, strrpos($charge->metadata->invoice, "_") - $pos - 1);
if (substr($eid,0,3) == "bal") {
$balance_payment = true;
watchdog('booking_debug', 'Balance payment via stripe for user with node id: !id and status !status.',
array('!id' => $nid, '!status' => $charge->status));
}
else {
watchdog('booking_debug', 'Initial payment via stripe for user with node id: !id and status !status.',
array('!id' => $nid, '!status' => $charge->status));
}
//this shouldn't ever happen, since stripe is sending this notification synchronously
//but just in case, check for an existing transaction that matches this one
$duplicate_check = db_query("SELECT payid, booking_person_nid FROM {booking_payment} where booking_ipn_track_id = :ipn_id ",
array(':ipn_id' => $charge->id))->fetchObject();
if ($duplicate_check) {
watchdog('booking', 'Detected duplicate stripe transaction notifications for transaction id !id, registration id !nid',
array('!id' => $charge->id, '!nid' => $nid), WATCHDOG_ERROR);
return;
}
$gross_amount = $charge->amount / 100;
$result = db_insert('booking_payment')
->fields(array(
'booking_person_nid' => $nid,
'booking_eventid' => $event->eid,
'booking_mc_gross' => $gross_amount,
'booking_mc_currency' => $charge->balance_transaction->currency,
'booking_mc_fee' => $charge->balance_transaction->fee / 100,
'booking_invoice' => $charge->metadata->invoice,
'booking_payer_id' => $charge->source->id,
'booking_payment_date' => $charge->created,
'booking_payment_status' => $charge->status,
'booking_first_name' => $charge->metadata->first_name,
'booking_last_name' => $charge->metadata->last_name,
'booking_buyer_email' => $charge->receipt_email,
'booking_item_name' => $charge->description,
'booking_ipn_track_id' => $charge->id,
'booking_stripe_response' => $charge->__toJSON(),
))
->execute();
//Get the person's info so we can update their total amount paid and booking status
$person = node_load($nid);
//check if we found a person matching this payment
if ($person) {
watchdog('booking', 'Found matching user with node id: !id; event id: !eid; existing payment !payment',
array('!id' => $nid, '!eid' => $eid, '!payment' => $person->booking_amount_paid));
_booking_process_person_payment($person, $gross_amount, $balance_payment);
}
else {
//couldn't find a matching nid for this invoice
watchdog('booking', "Unable to process payment for user with node id: '!id'", array('!id' => $nid), WATCHDOG_ERROR);
//db_query("UPDATE {booking_person} SET booking_tempid='' WHERE nid = %d", $nid);
}
}