_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', "
Stripe payment charge results:\n@info", 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', "
Detected exception processing Stripe payment (!message)\n@info", array('!message' => $e->getMessage(), '@info' => var_dump( $e->getTraceAsString() ))); drupal_set_message($e->getMessage(), 'error'); } catch (\Stripe\Error\ApiConnection $e) { watchdog('booking', "
Detected exception processing Stripe payment (!message)\n@info", array('!message' => $e->getMessage(), '@info' => var_dump( $e->getTraceAsString() ))); drupal_set_message($e->getMessage(), 'error'); } catch (\Stripe\Error\Api $e) { watchdog('booking', "
Detected exception processing Stripe payment (!message)\n@info", array('!message' => $e->getMessage(), '@info' => var_dump( $e->getTraceAsString() ))); drupal_set_message($e->getMessage(), 'error'); } catch (\Stripe\Exception\CardException $e) { watchdog('booking', "
Detected exception processing Stripe payment (!message)\n@info", array('!message' => $e->getMessage(), '@info' => var_dump( $e->getTraceAsString() ))); drupal_set_message($e->getMessage(), '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); } }