Compare commits
22 Commits
add-metada
...
add-metada
Author | SHA1 | Date | |
---|---|---|---|
bea988bc4c | |||
01a5a1b15f | |||
e17d91d6e3 | |||
19038bc7a0 | |||
d95fa6e721 | |||
06a97cf9c9 | |||
6a7858b6fa | |||
c5dc6d041b | |||
0b3a964634 | |||
db9d45834a | |||
aea105c1ab | |||
6324febf04 | |||
b0c82fbcc5 | |||
017b4b731c | |||
90baa97458 | |||
562e2a3698 | |||
ee03f3243d | |||
fed9880872 | |||
5920620f0d | |||
6d7a49f424 | |||
20c909cd49 | |||
db90890002 |
53
README.txt
53
README.txt
@ -1,4 +1,8 @@
|
|||||||
This is an Ubercart payment gateway module for Stripe.
|
This is an Ubercart payment gateway module for Stripe. It maintains PCI SAQ A
|
||||||
|
compliance which allows Stripe, the payment processor, to handle prcoessing and
|
||||||
|
storing of payment card details.
|
||||||
|
|
||||||
|
It is compliant with 3D Secure, 3D Secure 2, and Strong Customer Authentication (SCA)
|
||||||
|
|
||||||
Versions of the Stripe PHP Library and Stripe API that this module currently
|
Versions of the Stripe PHP Library and Stripe API that this module currently
|
||||||
supports are found in uc_stripe_libraries_info() in uc_stripe.module.
|
supports are found in uc_stripe_libraries_info() in uc_stripe.module.
|
||||||
@ -15,9 +19,9 @@ section, and enable the gateway under the Payment Gateways.
|
|||||||
c) On that page, provide your Stripe API keys, from
|
c) On that page, provide your Stripe API keys, from
|
||||||
https://dashboard.stripe.com/account/apikeys
|
https://dashboard.stripe.com/account/apikeys
|
||||||
|
|
||||||
d) Download and install the Stripe PHP Library version 2.2.0 or >=3.13.0
|
d) Download and install the Stripe PHP Library version 6.38.0 with stripe api
|
||||||
from https://github.com/stripe/stripe-php/releases. The recommended technique is
|
2019-05-16 or newer from https://github.com/stripe/stripe-php/releases. The
|
||||||
to use the command
|
recommended technique is to use the command
|
||||||
|
|
||||||
drush ldl stripe
|
drush ldl stripe
|
||||||
|
|
||||||
@ -25,8 +29,7 @@ If you don't use "drush ldl stripe", download and install the Stripe library in
|
|||||||
sites/all/libraries/stripe such that the path to VERSION
|
sites/all/libraries/stripe such that the path to VERSION
|
||||||
is sites/all/libraries/stripe/VERSION. YOU MUST CLEAR THE CACHE AFTER
|
is sites/all/libraries/stripe/VERSION. YOU MUST CLEAR THE CACHE AFTER
|
||||||
CHANGING THE STRIPE PHP LIBRARY. The Libraries module caches its memory of
|
CHANGING THE STRIPE PHP LIBRARY. The Libraries module caches its memory of
|
||||||
libraries like the Stripe Library. (Version 2.2.0 support is maintained for
|
libraries like the Stripe Library.
|
||||||
existing users; version 3.13.0+ supports PHP 7 and will get ongoing support.)
|
|
||||||
(With the latest version of the libraries module you can use the command:
|
(With the latest version of the libraries module you can use the command:
|
||||||
|
|
||||||
e) If you are using recurring payments, install version 2.x
|
e) If you are using recurring payments, install version 2.x
|
||||||
@ -48,14 +51,35 @@ disabled on admin/store/settings/payment/method/credit - uc_credit never sees
|
|||||||
the credit card number, so cannot properly validate it (and we don't want it to
|
the credit card number, so cannot properly validate it (and we don't want it to
|
||||||
ever know the credit card number.)
|
ever know the credit card number.)
|
||||||
|
|
||||||
Upgrading from uc_stripe 6.x-1.x or 7.x-1.x
|
i) uc_stripe creates it's own payment pane. Ensure the correct ordering by visiting
|
||||||
|
store->configuration->checkout (admin/store/settings/checkout).
|
||||||
|
|
||||||
|
Upgrading from uc_stripe 7.x-2.x
|
||||||
===========================================
|
===========================================
|
||||||
|
|
||||||
7.x-2.x does not use Stripe subscriptions for recurring payments, but instead
|
7.x-3.x maintains PCI SAQ A compliance and has major implementation changes from
|
||||||
uses the uc_recurring module. This means you have control of recurring
|
2.x. This version uses it's own payment pane in uc_cart to collect card info.
|
||||||
transactions without having to manage them on the Stripe dashboard. (Credit
|
The card fields such as card number, expiration date, and cvc code have all been
|
||||||
card numbers and sensitive data are *not* stored on your site; only the Stripe
|
hidden, and is handled entirely by Stripe's Element implementation.
|
||||||
customer ID is stored.)
|
Which means no credit card information gets processed at all by drupal. The last4,
|
||||||
|
and expiration date are sent back to drupal by Stripe's api.
|
||||||
|
|
||||||
|
7.x-3.x no longer creates a new stripe customer for each order. If a drupal user
|
||||||
|
already has a stripe customer ID, this module will attach future orders to that
|
||||||
|
exisiting stripe customer ID.
|
||||||
|
|
||||||
|
When upgrading from 2.x the ordering of the new stripe payment pane should be
|
||||||
|
verified at store->configuration->checkout (admin/store/settings/checkout).
|
||||||
|
|
||||||
|
An upgrade of the stripe library is required. See installation step d from above.
|
||||||
|
|
||||||
|
7.x-3.x Uses the uc_recurring module for recurring payments. It is also equipped
|
||||||
|
to handle recurring payments that require authentication (See the uc_recurring
|
||||||
|
steps below). Exisiting recurring payments set up with 7.x-2.x work without any
|
||||||
|
configuration changes.
|
||||||
|
|
||||||
|
Upgrading from uc_stripe 6.x-1.x or 7.x-1.x
|
||||||
|
============================================
|
||||||
|
|
||||||
The upgrade hooks, however, must move the customer id stored in the obsolete
|
The upgrade hooks, however, must move the customer id stored in the obsolete
|
||||||
uc_recurring_stripe table into the user table. When this happens the old
|
uc_recurring_stripe table into the user table. When this happens the old
|
||||||
@ -84,6 +108,11 @@ Recurring payments require automatically triggered renewals using
|
|||||||
uc_recurring_trigger_renewals ("Enabled triggered renewals" must be enabled
|
uc_recurring_trigger_renewals ("Enabled triggered renewals" must be enabled
|
||||||
on admin/store/settings/payment/edit/recurring)
|
on admin/store/settings/payment/edit/recurring)
|
||||||
|
|
||||||
|
You should also set your email message for recurring payments that require
|
||||||
|
Authentication. The system will email your customers with a link so that they
|
||||||
|
can authenticate and have their payment processed.
|
||||||
|
(You can edit from here: admin/store/settings/payment/edit/gateways)
|
||||||
|
|
||||||
If you were using Stripe subscriptions in v1 of this module, you may have to
|
If you were using Stripe subscriptions in v1 of this module, you may have to
|
||||||
disable those subscriptions in order to not double-charge your customers.
|
disable those subscriptions in order to not double-charge your customers.
|
||||||
|
|
||||||
|
@ -30,3 +30,5 @@ a.poweredbylink:hover {
|
|||||||
#uc_stripe_messages.hidden {display: none;}
|
#uc_stripe_messages.hidden {display: none;}
|
||||||
|
|
||||||
.stripe-warning {color: red; font-style: oblique; }
|
.stripe-warning {color: red; font-style: oblique; }
|
||||||
|
|
||||||
|
#edit-panes-payment-details-stripe-card-element{max-width: 600px}
|
||||||
|
304
js/uc_stripe.js
304
js/uc_stripe.js
@ -9,163 +9,185 @@
|
|||||||
Drupal.behaviors.uc_stripe = {
|
Drupal.behaviors.uc_stripe = {
|
||||||
attach: function (context) {
|
attach: function (context) {
|
||||||
|
|
||||||
|
// Once function prevents stripe from reloading. Any dom changes to stripe area will destroy element
|
||||||
|
// as a Stripe security feature
|
||||||
|
$('#uc-cart-checkout-form', context).once('uc_stripe', function(){
|
||||||
|
|
||||||
|
var stripe_card_element = '#stripe-card-element';
|
||||||
|
|
||||||
|
if (Drupal.settings && Drupal.settings.uc_stripe ) {
|
||||||
|
var apikey = Drupal.settings.uc_stripe.apikey;
|
||||||
|
|
||||||
|
var stripe = Stripe(apikey);
|
||||||
|
var elements = stripe.elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Map stripe names to (partial) Ubercart field names; Ubercart names add "billing_" or "shipping_" on the front.
|
// Map stripe names to (partial) Ubercart field names; Ubercart names add "billing_" or "shipping_" on the front.
|
||||||
const address_field_mapping = {
|
const address_field_mapping = {
|
||||||
"address_line1": "street1",
|
"address_line1": "street1",
|
||||||
"address_line2": "street2",
|
"address_line2": "street2",
|
||||||
"address_city": "city",
|
"address_city": "city",
|
||||||
"address_state": "zone",
|
"address_state": "zone",
|
||||||
"address_zip": "postal_code",
|
"address_zip": "postal_code",
|
||||||
"address_country": "country"
|
"address_country": "country"
|
||||||
};
|
};
|
||||||
var submitButton = $('.uc-cart-checkout-form #edit-continue');
|
var submitButton = $('.uc-cart-checkout-form #edit-continue');
|
||||||
|
|
||||||
var cc_container = $('.payment-details-credit');
|
// Load the js reference to these fields so that on the review page
|
||||||
var cc_num = cc_container.find(':input[id*="edit-panes-payment-details-cc-numbe"]');
|
// we can input the last 4 and expiration date which is returned to us by stripe paymentMethod call
|
||||||
var cc_cvv = cc_container.find(':input[id*="edit-panes-payment-details-cc-cv"]');
|
var cc_container = $('.payment-details-credit');
|
||||||
|
var cc_num = cc_container.find(':input[id*="edit-panes-payment-details-cc-numbe"]');
|
||||||
|
var cc_cvv = cc_container.find(':input[id*="edit-panes-payment-details-cc-cv"]');
|
||||||
|
var cc_exp_month = cc_container.find('#edit-panes-payment-details-cc-exp-month');
|
||||||
|
var cc_exp_year = cc_container.find('#edit-panes-payment-details-cc-exp-year');
|
||||||
|
|
||||||
// Make sure that when the page is being loaded the token value is reset
|
// Make sure that when the page is being loaded the paymentMethod value is reset
|
||||||
// Browser or other caching might do otherwise.
|
// Browser or other caching might do otherwise.
|
||||||
$("[name='panes[payment][details][stripe_token]']").val('default');
|
$("[name='panes[payment-stripe][details][stripe_payment_method]']").val('default');
|
||||||
|
|
||||||
$('span#stripe-nojs-warning').parent().hide();
|
// JS must enable the button; otherwise form might disclose cc info. It starts disabled
|
||||||
|
submitButton.attr('disabled', false);
|
||||||
|
|
||||||
// JS must enable the button; otherwise form might disclose cc info. It starts disabled
|
// When this behavior fires, we can clean the form so it will behave properly,
|
||||||
submitButton.attr('disabled', false);
|
// Remove 'name' from sensitive form elements so there's no way they can be submitted.
|
||||||
|
cc_num.removeAttr('name').removeAttr('disabled');
|
||||||
// When this behavior fires, we can clean the form so it will behave properly,
|
$('div.form-item-panes-payment-details-cc-number').removeClass('form-disabled');
|
||||||
// Remove 'name' from sensitive form elements so there's no way they can be submitted.
|
cc_cvv.removeAttr('name').removeAttr('disabled');
|
||||||
cc_num.removeAttr('name').removeAttr('disabled');
|
var cc_val_val = cc_num.val();
|
||||||
$('div.form-item-panes-payment-details-cc-number').removeClass('form-disabled');
|
if (cc_val_val && cc_val_val.indexOf('Last 4')) {
|
||||||
cc_cvv.removeAttr('name').removeAttr('disabled');
|
cc_num.val('');
|
||||||
var cc_val_val = cc_num.val();
|
|
||||||
if (cc_val_val && cc_val_val.indexOf('Last 4')) {
|
|
||||||
cc_num.val('');
|
|
||||||
}
|
|
||||||
|
|
||||||
submitButton.click(function (e) {
|
|
||||||
|
|
||||||
// We must find the various fields again, because they may have been swapped
|
|
||||||
// in by ajax action of the form.
|
|
||||||
cc_container = $('.payment-details-credit');
|
|
||||||
cc_num = cc_container.find(':input[id*="edit-panes-payment-details-cc-numbe"]');
|
|
||||||
cc_cvv = cc_container.find(':input[id*="edit-panes-payment-details-cc-cv"]');
|
|
||||||
|
|
||||||
// If not credit card processing or no token field, just let the submit go on
|
|
||||||
// Also continue if we've received the tokenValue
|
|
||||||
var tokenField = $("[name='panes[payment][details][stripe_token]']");
|
|
||||||
if (!$("div.payment-details-credit").length || !tokenField.length || tokenField.val().indexOf('tok_') == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've requested and are waiting for token, prevent any further submit
|
|
||||||
if (tokenField.val() == 'requested') {
|
|
||||||
return false; // Prevent any submit processing until token is received
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go ahead and request the token
|
// Custom styling can be passed to options when creating an Element.
|
||||||
tokenField.val('requested');
|
var style = {
|
||||||
|
base: {
|
||||||
try {
|
// Add your base input styles here. For example:
|
||||||
var name = undefined;
|
fontSize: '24px',
|
||||||
|
color: "#000000",
|
||||||
if ($(':input[name="panes[billing][billing_first_name]"]').length) {
|
iconColor: "blue",
|
||||||
name = $(':input[name="panes[billing][billing_first_name]"]').val() + " " + $(':input[name="panes[billing][billing_last_name]"]').val();
|
|
||||||
}
|
}
|
||||||
if (typeof name === "undefined" && $(':input[name="panes[delivery][delivery_first_name]"]').length) {
|
};
|
||||||
name = $(':input[name="panes[delivery][delivery_first_name]"]').val() + " " + $(':input[name="panes[delivery][delivery_last_name]"]').val();
|
|
||||||
|
// Create an instance of the card Element.
|
||||||
|
var card = elements.create('card', {style: style});
|
||||||
|
|
||||||
|
// Add an instance of the card Element into the #stripe-card-element <div>.
|
||||||
|
card.mount(stripe_card_element);
|
||||||
|
|
||||||
|
// Display errors from stripe
|
||||||
|
card.addEventListener('change', function(event) {
|
||||||
|
var displayError = document.getElementById('uc_stripe_messages');
|
||||||
|
if (event.error) {
|
||||||
|
displayError.textContent = event.error.message;
|
||||||
|
console.log(event.error.message)
|
||||||
|
} else {
|
||||||
|
displayError.textContent = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
submitButton.click(function (e) {
|
||||||
|
|
||||||
|
// We must find the various fields again, because they may have been swapped
|
||||||
|
// in by ajax action of the form.
|
||||||
|
cc_container = $('.payment-details-credit');
|
||||||
|
cc_num = cc_container.find(':input[id*="edit-panes-payment-details-cc-numbe"]');
|
||||||
|
cc_cvv = cc_container.find(':input[id*="edit-panes-payment-details-cc-cv"]');
|
||||||
|
cc_exp_year = cc_container.find('#edit-panes-payment-details-cc-exp-month');
|
||||||
|
cc_exp_month = cc_container.find('#edit-panes-payment-details-cc-exp-year');
|
||||||
|
|
||||||
|
// If not credit card processing or no payment method field, just let the submit go on
|
||||||
|
// Also continue if we've received the tokenValue
|
||||||
|
var paymentMethodField = $("[name='panes[payment-stripe][details][stripe_payment_method]']");
|
||||||
|
if (!$("div.payment-details-credit").length || !paymentMethodField.length || paymentMethodField.val().indexOf('pm_') == 0) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var params = {
|
// If we've requested and are waiting for token, prevent any further submit
|
||||||
number: cc_num.val(),
|
if (paymentMethodField.val() == 'requested') {
|
||||||
cvc: cc_cvv.val(),
|
return false; // Prevent any submit processing until token is received
|
||||||
exp_month: $(':input[name="panes[payment][details][cc_exp_month]"]').val(),
|
}
|
||||||
exp_year: $(':input[name="panes[payment][details][cc_exp_year]"]').val(),
|
|
||||||
name: name
|
|
||||||
};
|
|
||||||
|
|
||||||
// Translate the Ubercart billing/shipping fields to Stripe values
|
// Go ahead and request the token
|
||||||
for (var key in address_field_mapping) {
|
paymentMethodField.val('requested');
|
||||||
const prefixes = ['billing', 'delivery'];
|
|
||||||
for (var i = 0; i < prefixes.length; i++) {
|
try {
|
||||||
var prefix = prefixes[i];
|
|
||||||
var uc_field_name = prefix + '_' + address_field_mapping[key];
|
stripe.createPaymentMethod('card', card).then(function (response) {
|
||||||
var location = ':input[name="panes[' + prefix + '][' + uc_field_name + ']"]';
|
|
||||||
if ($(location).length) {
|
if (response.error) {
|
||||||
params[key] = $(location).val();
|
|
||||||
if ($(location).attr('type') == 'select-one') {
|
// Show the errors on the form
|
||||||
params[key] = $(location + " option:selected").text();
|
$('#uc_stripe_messages')
|
||||||
}
|
.removeClass("hidden")
|
||||||
break; // break out of billing/shipping loop because we got the info
|
.text(response.error.message);
|
||||||
|
$('#edit-stripe-messages').val(response.error.message);
|
||||||
|
|
||||||
|
// Make the fields visible again for retry
|
||||||
|
cc_num
|
||||||
|
.css('visibility', 'visible')
|
||||||
|
.val('')
|
||||||
|
.attr('name', 'panes[payment][details][cc_number]');
|
||||||
|
cc_cvv
|
||||||
|
.css('visibility', 'visible')
|
||||||
|
.val('')
|
||||||
|
.attr('name', 'panes[payment][details][cc_cvv]');
|
||||||
|
|
||||||
|
|
||||||
|
// Turn off the throbber
|
||||||
|
$('.ubercart-throbber').remove();
|
||||||
|
// Remove the bogus copy of the submit button added in uc_cart.js ucSubmitOrderThrobber
|
||||||
|
submitButton.next().remove();
|
||||||
|
// And show the hidden original button which has the behavior attached to it.
|
||||||
|
submitButton.show();
|
||||||
|
|
||||||
|
paymentMethodField.val('default'); // Make sure token field set back to default
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// token contains id, last4, and card type
|
||||||
|
var paymentMethodId = response.paymentMethod.id;
|
||||||
|
|
||||||
|
|
||||||
|
// Insert the token into the form so it gets submitted to the server
|
||||||
|
paymentMethodField.val(paymentMethodId);
|
||||||
|
|
||||||
|
// set cc expiration date received from stripe so that it is available on checkout review
|
||||||
|
cc_exp_year.val(response.paymentMethod.card.exp_month);
|
||||||
|
cc_exp_month.val(response.paymentMethod.card.exp_year);
|
||||||
|
|
||||||
|
// Since we're now submitting, make sure that uc_credit doesn't
|
||||||
|
// find values it objects to; after "fixing" set the name back on the
|
||||||
|
// form element.
|
||||||
|
// add dummy tweleve 5's and the last 4 of credit card so that last 4 show
|
||||||
|
cc_num
|
||||||
|
.css('visibility', 'hidden')
|
||||||
|
.val('555555555555' + response.paymentMethod.card.last4)
|
||||||
|
.attr('name', 'panes[payment][details][cc_number]');
|
||||||
|
cc_cvv
|
||||||
|
.css('visibility', 'hidden')
|
||||||
|
.val('999')
|
||||||
|
.attr('name', 'panes[payment][details][cc_cvv]');
|
||||||
|
|
||||||
|
// now actually submit to Drupal. The only "real" things going
|
||||||
|
// are the token and the expiration date and last 4 of cc
|
||||||
|
submitButton.click();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
} catch (e) {
|
||||||
|
$('#uc_stripe_messages')
|
||||||
|
.removeClass("hidden")
|
||||||
|
.text(e.message);
|
||||||
|
$('#edit-stripe-messages').val(e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stripe.createToken(params, function (status, response) {
|
// Prevent processing until we get the token back
|
||||||
|
return false;
|
||||||
if (response.error) {
|
});
|
||||||
|
|
||||||
// Show the errors on the form
|
|
||||||
$('#uc_stripe_messages')
|
|
||||||
.removeClass("hidden")
|
|
||||||
.text(response.error.message);
|
|
||||||
$('#edit-stripe-messages').val(response.error.message);
|
|
||||||
|
|
||||||
// Make the fields visible again for retry
|
|
||||||
cc_num
|
|
||||||
.css('visibility', 'visible')
|
|
||||||
.val('')
|
|
||||||
.attr('name', 'panes[payment][details][cc_number]');
|
|
||||||
cc_cvv
|
|
||||||
.css('visibility', 'visible')
|
|
||||||
.val('')
|
|
||||||
.attr('name', 'panes[payment][details][cc_cvv]');
|
|
||||||
|
|
||||||
|
|
||||||
// Turn off the throbber
|
|
||||||
$('.ubercart-throbber').remove();
|
|
||||||
// Remove the bogus copy of the submit button added in uc_cart.js ucSubmitOrderThrobber
|
|
||||||
submitButton.next().remove();
|
|
||||||
// And show the hidden original button which has the behavior attached to it.
|
|
||||||
submitButton.show();
|
|
||||||
|
|
||||||
tokenField.val('default'); // Make sure token field set back to default
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// token contains id, last4, and card type
|
|
||||||
var token = response.id;
|
|
||||||
|
|
||||||
// Insert the token into the form so it gets submitted to the server
|
|
||||||
tokenField.val(token);
|
|
||||||
|
|
||||||
// Since we're now submitting, make sure that uc_credit doesn't
|
|
||||||
// find values it objects to; after "fixing" set the name back on the
|
|
||||||
// form element.
|
|
||||||
cc_num
|
|
||||||
.css('visibility', 'hidden')
|
|
||||||
.val('555555555555' + response.card.last4)
|
|
||||||
.attr('name', 'panes[payment][details][cc_number]');
|
|
||||||
cc_cvv
|
|
||||||
.css('visibility', 'hidden')
|
|
||||||
.val('999')
|
|
||||||
.attr('name', 'panes[payment][details][cc_cvv]');
|
|
||||||
|
|
||||||
// now actually submit to Drupal. The only "real" things going
|
|
||||||
// are the token and the expiration date.
|
|
||||||
submitButton.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
$('#uc_stripe_messages')
|
|
||||||
.removeClass("hidden")
|
|
||||||
.text(e.message);
|
|
||||||
$('#edit-stripe-messages').val(e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent processing until we get the token back
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
|
91
js/uc_stripe_process_payment.js
Normal file
91
js/uc_stripe_process_payment.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* uc_stripe.js
|
||||||
|
*
|
||||||
|
* Handles all interactions with Stripe on the client side for PCI-DSS compliance
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
|
||||||
|
Drupal.behaviors.uc_stripe_process_payment = {
|
||||||
|
attach: function (context) {
|
||||||
|
|
||||||
|
$('#uc-cart-checkout-review-form, #uc-stripe-authenticate-payment-form', context).once('uc_stripe', function(){
|
||||||
|
|
||||||
|
if (Drupal.settings && Drupal.settings.uc_stripe ) {
|
||||||
|
var apikey = Drupal.settings.uc_stripe.apikey;
|
||||||
|
var methodId = Drupal.settings.uc_stripe.methodId;
|
||||||
|
var orderId = Drupal.settings.uc_stripe.orderId
|
||||||
|
var stripe = Stripe(apikey);
|
||||||
|
}
|
||||||
|
|
||||||
|
var submitButton = $('#edit-submit');
|
||||||
|
var processed = false;
|
||||||
|
|
||||||
|
submitButton.click(function (e) {
|
||||||
|
if(!processed){
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/uc_stripe/ajax/confirm_payment',
|
||||||
|
type: "POST",
|
||||||
|
data: JSON.stringify({ payment_method_id: methodId, order_id: orderId }),
|
||||||
|
contentType: 'application/json;',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(result){
|
||||||
|
handleServerResponse(result);
|
||||||
|
},
|
||||||
|
error: function(result){
|
||||||
|
handleServerResponse(result);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleServerResponse(response) {
|
||||||
|
if (response.error) {
|
||||||
|
processed = true;
|
||||||
|
submitButton.click();
|
||||||
|
// 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
|
||||||
|
processed = true;
|
||||||
|
submitButton.click();
|
||||||
|
} else {
|
||||||
|
// The card action has been handled
|
||||||
|
// The PaymentIntent can be confirmed again on the server
|
||||||
|
$.ajax({
|
||||||
|
url: '/uc_stripe/ajax/confirm_payment',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify({ payment_intent_id: result.paymentIntent.id, order_id: orderId }),
|
||||||
|
contentType: 'application/json;',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(confirmResult){
|
||||||
|
return handleServerResponse(confirmResult);
|
||||||
|
},
|
||||||
|
error: function(confirmResult){
|
||||||
|
return handleServerResponse(confirmResult);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Show success message
|
||||||
|
processed = true;
|
||||||
|
submitButton.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}(jQuery));
|
@ -6,11 +6,3 @@ dependencies[] = libraries
|
|||||||
package = Ubercart - payment
|
package = Ubercart - payment
|
||||||
core = 7.x
|
core = 7.x
|
||||||
php = 5.3
|
php = 5.3
|
||||||
|
|
||||||
|
|
||||||
; Information added by Drupal.org packaging script on 2017-05-19
|
|
||||||
version = "7.x-2.2+2-dev"
|
|
||||||
core = "7.x"
|
|
||||||
project = "uc_stripe"
|
|
||||||
datestamp = "1495159090"
|
|
||||||
|
|
||||||
|
@ -92,6 +92,57 @@ function uc_stripe_install() {
|
|||||||
variable_set('uc_credit_validate_numbers', FALSE);
|
variable_set('uc_credit_validate_numbers', FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_uninstall().
|
||||||
|
*/
|
||||||
|
function uc_stripe_uninstall() {
|
||||||
|
variable_del('uc_stripe_authentication_required_email');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_schema().
|
||||||
|
*/
|
||||||
|
function uc_stripe_schema() {
|
||||||
|
$schema['uc_stripe_pending_auth'] = array(
|
||||||
|
'description' => 'Ubercart Stripe - Track orders pending authentication',
|
||||||
|
'fields' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => 'id of entry',
|
||||||
|
'type' => 'serial',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'order_id' => array(
|
||||||
|
'description' => 'Order Id of pending order',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'rfee_id' => array(
|
||||||
|
'description' => 'Recurring Fee Id of pending order',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'completed' => array(
|
||||||
|
'description' => 'Competion status of this pending order',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'hash' => array (
|
||||||
|
'description' => 'The unqiue has of order and payment id',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => '100',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'unique keys' => array(
|
||||||
|
'hash' => array(
|
||||||
|
'hash'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'primary key' => array('id'),
|
||||||
|
);
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable triggered renewals, as uc_recurring manages renewals with this version.
|
* Enable triggered renewals, as uc_recurring manages renewals with this version.
|
||||||
*/
|
*/
|
||||||
@ -181,3 +232,61 @@ function _uc_stripe_move_customer_id(&$sandbox) {
|
|||||||
$sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
|
$sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates table to track orders that require extra authentication verification.
|
||||||
|
*/
|
||||||
|
function uc_stripe_update_7301() {
|
||||||
|
|
||||||
|
$table = array(
|
||||||
|
'description' => 'Ubercart Stripe - Track orders pending authentication',
|
||||||
|
'fields' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => 'id of entry',
|
||||||
|
'type' => 'serial',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'order_id' => array(
|
||||||
|
'description' => 'Order Id of pending order',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'rfee_id' => array(
|
||||||
|
'description' => 'Recurring Fee Id of pending order',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'completed' => array(
|
||||||
|
'description' => 'Competion status of this pending order',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
'hash' => array(
|
||||||
|
'description' => 'The unqiue has of order and payment id',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => '100',
|
||||||
|
'not null' => TRUE
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'unique keys' => array(
|
||||||
|
'hash' => array(
|
||||||
|
'hash'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'primary key' => array('id'),
|
||||||
|
);
|
||||||
|
|
||||||
|
db_create_table('uc_stripe_pending_auth', $table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes typo in variable uc_stripe_authenticaiton_required_email.
|
||||||
|
*/
|
||||||
|
function uc_stripe_update_7302() {
|
||||||
|
$typo_var_name = 'uc_stripe_authenticaiton_required_email';
|
||||||
|
$value = variable_get($typo_var_name, '');
|
||||||
|
if (!empty($value)) {
|
||||||
|
variable_set('uc_stripe_authentication_required_email', $value);
|
||||||
|
variable_del($typo_var_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
62
uc_stripe.mail.inc
Normal file
62
uc_stripe.mail.inc
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns the default off session authention email text.
|
||||||
|
* @return $text - Email text
|
||||||
|
*/
|
||||||
|
function _uc_stripe_get_authentication_required_email_text(){
|
||||||
|
$text = t("Dear [user:name],
|
||||||
|
|
||||||
|
We were unable to process your subscription payment.
|
||||||
|
|
||||||
|
Your financial institution is requesting additional verification before your subscription can be renewed.
|
||||||
|
|
||||||
|
Please visit this link to return to our site and complete the verification step.
|
||||||
|
|
||||||
|
[uc_stripe:verification-link]
|
||||||
|
|
||||||
|
-- [site:name] team
|
||||||
|
|
||||||
|
");
|
||||||
|
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Token callback that adds the authentication link to user mails.
|
||||||
|
*
|
||||||
|
* This function is used by the token_replace() call in uc_stripe_mail() to add
|
||||||
|
* the url to verify payment information
|
||||||
|
*
|
||||||
|
* @param $replacements
|
||||||
|
* An associative array variable containing mappings from token names to
|
||||||
|
* values (for use with strtr()).
|
||||||
|
* @param $data
|
||||||
|
* An associative array of token replacement values.
|
||||||
|
* @param $options
|
||||||
|
* Unused parameter required by the token_replace() function. */
|
||||||
|
function uc_stripe_mail_tokens(&$replacements, $data, $options) {
|
||||||
|
global $base_url;
|
||||||
|
$replacements['[uc_stripe:verification-link]'] = $base_url.'/stripe/authenticate-payment/'.$data['authentication_key'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_mail().
|
||||||
|
*
|
||||||
|
* Send mail and replace token with authenticaion link.
|
||||||
|
*/
|
||||||
|
function uc_stripe_mail($key, &$message, $params) {
|
||||||
|
|
||||||
|
switch ($key) {
|
||||||
|
case 'authentication_required' :
|
||||||
|
|
||||||
|
$message['subject'] = t('Additional Verification Required to Process Subscription.');
|
||||||
|
$variables = array('user' => $params['user'], 'authentication_key' => $params['hash']);
|
||||||
|
|
||||||
|
$message['body'][]= token_replace($params['body'], $variables, array('language' => language_default(), 'callback' => 'uc_stripe_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
719
uc_stripe.module
719
uc_stripe.module
@ -14,83 +14,57 @@
|
|||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function uc_stripe_libraries_info() {
|
|
||||||
|
|
||||||
|
module_load_include('inc', 'uc_stripe', 'uc_stripe.mail');
|
||||||
|
|
||||||
|
function uc_stripe_libraries_info() {
|
||||||
$libraries['stripe'] = array(
|
$libraries['stripe'] = array(
|
||||||
'name' => 'Stripe PHP Library',
|
'name' => 'Stripe PHP Library',
|
||||||
'vendor url' => 'http://stripe.com',
|
'vendor url' => 'http://stripe.com',
|
||||||
'download url' => 'https://github.com/stripe/stripe-php/releases',
|
'download url' => 'https://github.com/stripe/stripe-php/releases',
|
||||||
'download file url' => 'https://github.com/stripe/stripe-php/archive/v3.20.0.tar.gz',
|
'download file url' => 'https://github.com/stripe/stripe-php/archive/v6.38.0.tar.gz',
|
||||||
'version arguments' => array(
|
'version arguments' => array(
|
||||||
'file' => 'VERSION',
|
'file' => 'VERSION',
|
||||||
'pattern' => '/(\d+\.\d+\.\d+)/',
|
'pattern' => '/(\d+\.\d+\.\d+)/',
|
||||||
),
|
),
|
||||||
'versions' => array(
|
'versions' => array(
|
||||||
'2.2.0' => array(
|
'6.38.0' => array(
|
||||||
'files' => array(
|
|
||||||
'php' => array(
|
|
||||||
'lib/Util/RequestOptions.php',
|
|
||||||
'lib/Util/Set.php',
|
|
||||||
'lib/Util/Util.php',
|
|
||||||
'lib/Object.php',
|
|
||||||
'lib/ApiResource.php',
|
|
||||||
'lib/Account.php',
|
|
||||||
'lib/ExternalAccount.php',
|
|
||||||
'lib/AlipayAccount.php',
|
|
||||||
'lib/ApiRequestor.php',
|
|
||||||
'lib/ApplicationFee.php',
|
|
||||||
'lib/ApplicationFeeRefund.php',
|
|
||||||
'lib/AttachedObject.php',
|
|
||||||
'lib/SingletonApiResource.php',
|
|
||||||
'lib/Balance.php',
|
|
||||||
'lib/BalanceTransaction.php',
|
|
||||||
'lib/BankAccount.php',
|
|
||||||
'lib/BitcoinReceiver.php',
|
|
||||||
'lib/BitcoinTransaction.php',
|
|
||||||
'lib/Card.php',
|
|
||||||
'lib/Charge.php',
|
|
||||||
'lib/Collection.php',
|
|
||||||
'lib/Coupon.php',
|
|
||||||
'lib/Customer.php',
|
|
||||||
'lib/Error/Base.php',
|
|
||||||
'lib/Error/Api.php',
|
|
||||||
'lib/Error/ApiConnection.php',
|
|
||||||
'lib/Error/Authentication.php',
|
|
||||||
'lib/Error/Card.php',
|
|
||||||
'lib/Error/InvalidRequest.php',
|
|
||||||
'lib/Error/RateLimit.php',
|
|
||||||
'lib/Event.php',
|
|
||||||
'lib/FileUpload.php',
|
|
||||||
'lib/HttpClient/ClientInterface.php',
|
|
||||||
'lib/HttpClient/CurlClient.php',
|
|
||||||
'lib/Invoice.php',
|
|
||||||
'lib/InvoiceItem.php',
|
|
||||||
'lib/Plan.php',
|
|
||||||
'lib/Recipient.php',
|
|
||||||
'lib/Refund.php',
|
|
||||||
'lib/Stripe.php',
|
|
||||||
'lib/Subscription.php',
|
|
||||||
'lib/Token.php',
|
|
||||||
'lib/Transfer.php',
|
|
||||||
'lib/TransferReversal.php',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'stripe_api_version' => '2015-06-15',
|
|
||||||
),
|
|
||||||
'3.0' => array(
|
|
||||||
'files' => array(
|
'files' => array(
|
||||||
'php' => array(
|
'php' => array(
|
||||||
'init.php',
|
'init.php',
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
'stripe_api_version' => '2016-03-07',
|
'stripe_api_version' => '2019-05-16'
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $libraries;
|
return $libraries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu().
|
||||||
|
*/
|
||||||
|
function uc_stripe_menu() {
|
||||||
|
$items = array();
|
||||||
|
|
||||||
|
$items['uc_stripe/ajax/confirm_payment'] = array(
|
||||||
|
'access callback' => true,
|
||||||
|
'page callback' => '_uc_stripe_confirm_payment',
|
||||||
|
'delivery callback' => 'drupal_json_output',
|
||||||
|
);
|
||||||
|
|
||||||
|
$items['stripe/authenticate-payment/%'] = array(
|
||||||
|
'access callback' => true,
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('uc_stripe_authenticate_payment_form', 2),
|
||||||
|
'file' => 'uc_stripe.pages.inc'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_payment_gateway to register this payment gateway
|
* Implements hook_payment_gateway to register this payment gateway
|
||||||
* @return array
|
* @return array
|
||||||
@ -131,6 +105,47 @@ function uc_stripe_recurring_info() {
|
|||||||
return $items;
|
return $items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_form_FORMID_alter() to do JS Stripe processing when processing
|
||||||
|
* from the order review page
|
||||||
|
*
|
||||||
|
* @param unknown $form
|
||||||
|
* @param unknown $form_state
|
||||||
|
* @param unknown $form_id
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function uc_stripe_form_uc_cart_checkout_review_form_alter(&$form, &$form_state, $form_id){
|
||||||
|
|
||||||
|
//This alter hook should only take action when payment method is credit.
|
||||||
|
if($form_state['uc_order']->payment_method != 'credit'){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If payment method is not found, hide submit button, and show error to user
|
||||||
|
if (empty($_SESSION['stripe']['payment_method'])) {
|
||||||
|
$form['actions']['submit']['#type'] = 'hidden';
|
||||||
|
$fail_message = variable_get('uc_credit_fail_message', t('We were unable to process your credit card payment. Please verify your details and try again. If the problem persists, contact us to complete your order.'));
|
||||||
|
watchdog('uc_stripe', 'Stripe charge failed for order @order, message: @message', array('@order' => $form_state['uc_order']->order_id, '@message' => 'Payment method not found'));
|
||||||
|
drupal_set_message($fail_message, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a payment fails, remove the Submit Order button because it will most
|
||||||
|
// likely fail again. Instead, the customer should hit back to try again.
|
||||||
|
if(isset($_SESSION['stripe']['payment_failed'])){
|
||||||
|
$form['actions']['submit']['#type'] = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
$stripe_payment_method_id = $_SESSION['stripe']['payment_method'];
|
||||||
|
$order_id = $form_state['uc_order']->order_id;
|
||||||
|
$apikey = variable_get('uc_stripe_testmode', TRUE) ? check_plain(variable_get('uc_stripe_api_key_test_publishable', '')) : check_plain(variable_get('uc_stripe_api_key_live_publishable', ''));
|
||||||
|
$settings = array('methodId' => $stripe_payment_method_id, 'apikey' => $apikey, 'orderId' => $order_id);
|
||||||
|
|
||||||
|
//Attach Stripe v3 JS library and JS for processing payment
|
||||||
|
$form['#attached']['js']['https://js.stripe.com/v3/'] = array('type' => 'external');
|
||||||
|
$form['#attached']['js'][] = array('data' => array('uc_stripe' => $settings), 'type' => 'setting');
|
||||||
|
$form['#attached']['js'][] = drupal_get_path('module', 'uc_stripe') . '/js/uc_stripe_process_payment.js';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_form_FORMID_alter() to change the checkout form
|
* Implements hook_form_FORMID_alter() to change the checkout form
|
||||||
@ -142,37 +157,49 @@ function uc_stripe_recurring_info() {
|
|||||||
*/
|
*/
|
||||||
function uc_stripe_form_uc_cart_checkout_form_alter(&$form, &$form_state) {
|
function uc_stripe_form_uc_cart_checkout_form_alter(&$form, &$form_state) {
|
||||||
|
|
||||||
|
$form['panes']['payment-stripe']['#attached']['css'][] = array(
|
||||||
|
'data' => '#payment-stripe-pane { display: none; }',
|
||||||
|
'type' => 'inline',
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['panes']['payment-stripe']['#states'] = array(
|
||||||
|
'visible' => array(
|
||||||
|
':input[name="panes[payment][payment_method]"]' => array(
|
||||||
|
'value' => 'credit'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$stripe_payment_form = &$form['panes']['payment-stripe']['details'];
|
||||||
$payment_form = &$form['panes']['payment']['details'];
|
$payment_form = &$form['panes']['payment']['details'];
|
||||||
|
|
||||||
$payment_form['stripe_nojs_warning'] = array(
|
// Markup text will not be displayed when JS and stripe are functioning properly
|
||||||
'#type' => 'item',
|
// since Stripe Elements will replace the contents of this div
|
||||||
'#markup' => '<span id="stripe-nojs-warning" class="stripe-warning">' . t('Sorry, for security reasons your card cannot be processed because Javascript is disabled in your browser.') . '</span>',
|
$stripe_payment_form['stripe_card_element'] = array(
|
||||||
'#weight' => -1000,
|
'#prefix' => '<div id="stripe-card-element">',
|
||||||
|
'#weight' => - 10,
|
||||||
|
'#markup' => '<div class="stripe-warning">' . t('Sorry, for security reasons your card cannot be processed. Please refresh this page and try again. If the problem persists please check that Javascript is enabled your browser.') . '</div>',
|
||||||
|
'#suffix' => '</div>',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Powered by Stripe (logo from https://stripe.com/about/resources)
|
// Powered by Stripe (logo from https://stripe.com/about/resources)
|
||||||
if (variable_get('uc_stripe_poweredby', FALSE)) {
|
if (variable_get('uc_stripe_poweredby', FALSE)) {
|
||||||
$payment_form['field_message'] = array(
|
$payment_form['field_message'] = array(
|
||||||
'#type' => 'item',
|
'#type' => 'item',
|
||||||
'#markup' => "<a href='http://stripe.com'><img src=" . '/' . drupal_get_path('module', 'uc_stripe') . '/images/solid-dark.svg' . " alt='Powered by Stripe'></a>",
|
'#markup' => "<a target='_blank' href='http://stripe.com'><img src=" . '/' . drupal_get_path('module', 'uc_stripe') . '/images/solid-dark.svg' . " alt='Powered by Stripe'></a>",
|
||||||
'#weight' => 1,
|
'#weight' => 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$payment_form['stripe_token'] = array(
|
// Used for payment method Id when retrieved from stripe.
|
||||||
|
$stripe_payment_form['stripe_payment_method'] = array(
|
||||||
'#type' => 'hidden',
|
'#type' => 'hidden',
|
||||||
'#default_value' => 'default',
|
'#default_value' => 'default',
|
||||||
'#attributes' => array(
|
'#attributes' => array(
|
||||||
'id' => 'edit-panes-payment-details-stripe-token',
|
'id' => 'edit-panes-stripe-payment-details-stripe-payment-method',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prevent form Credit card fill and submission if javascript has not removed
|
|
||||||
// the "disabled" attributes..
|
|
||||||
// If JS happens to be disabled, we don't want user to be able to enter CC data.
|
|
||||||
// Note that we can't use '#disabled', as it causes Form API to discard all input,
|
|
||||||
// so use the disabled attribute instead.
|
|
||||||
$form['panes']['payment']['details']['cc_number']['#attributes']['disabled'] = 'disabled';
|
|
||||||
if (empty($form['actions']['continue']['#attributes'])) {
|
if (empty($form['actions']['continue']['#attributes'])) {
|
||||||
$form['actions']['continue']['#attributes'] = array();
|
$form['actions']['continue']['#attributes'] = array();
|
||||||
}
|
}
|
||||||
@ -180,13 +207,38 @@ function uc_stripe_form_uc_cart_checkout_form_alter(&$form, &$form_state) {
|
|||||||
|
|
||||||
$apikey = variable_get('uc_stripe_testmode', TRUE) ? check_plain(variable_get('uc_stripe_api_key_test_publishable', '')) : check_plain(variable_get('uc_stripe_api_key_live_publishable', ''));
|
$apikey = variable_get('uc_stripe_testmode', TRUE) ? check_plain(variable_get('uc_stripe_api_key_test_publishable', '')) : check_plain(variable_get('uc_stripe_api_key_live_publishable', ''));
|
||||||
|
|
||||||
|
|
||||||
// Add custom JS and CSS
|
// Add custom JS and CSS
|
||||||
$form['#attached']['js']['https://js.stripe.com/v2/'] = array('type' => 'external');
|
$settings = array('apikey' => $apikey);
|
||||||
$form['#attached']['js'][] = array('data' => "Stripe.setPublishableKey('$apikey')", 'type' => 'inline');
|
$form['#attached']['js']['https://js.stripe.com/v3/'] = array('type' => 'external');
|
||||||
|
$form['#attached']['js'][] = array('data' => array('uc_stripe' => $settings), 'type' => 'setting');
|
||||||
$form['#attached']['js'][] = drupal_get_path('module', 'uc_stripe') . '/js/uc_stripe.js';
|
$form['#attached']['js'][] = drupal_get_path('module', 'uc_stripe') . '/js/uc_stripe.js';
|
||||||
$form['#attached']['css'][] = drupal_get_path('module', 'uc_stripe') . '/css/uc_stripe.css';
|
$form['#attached']['css'][] = drupal_get_path('module', 'uc_stripe') . '/css/uc_stripe.css';
|
||||||
|
|
||||||
|
// hide cc fields and set defaults since we rely fully on stripe's dynamic cc fields
|
||||||
|
$payment_form['cc_number']['#type'] = 'hidden';
|
||||||
|
$payment_form['cc_number']['#default_value'] = '';
|
||||||
|
$payment_form['cc_number']['#attributes']['id'] = 'edit-panes-payment-details-cc-number';
|
||||||
|
|
||||||
|
$payment_form['cc_cvv']['#type'] = 'hidden';
|
||||||
|
$payment_form['cc_cvv']['#default_value'] = '';
|
||||||
|
$payment_form['cc_cvv']['#attributes']['id'] = 'edit-panes-payment-details-cc-cvv';
|
||||||
|
|
||||||
|
$payment_form['cc_exp_year']['#type'] = 'hidden';
|
||||||
|
$payment_form['cc_exp_year']['#attributes']['id'] = 'edit-panes-payment-details-cc-exp-year';
|
||||||
|
|
||||||
|
//Stripe CC expiration can be up to 50 years in future. The normal ubercart select
|
||||||
|
// options only go up to 20 years in the future.
|
||||||
|
$min = intval(date('Y'));
|
||||||
|
$max = intval(date('Y')) + 50;
|
||||||
|
$default = intval(date('Y'));
|
||||||
|
|
||||||
|
$payment_form['cc_exp_year']['#options'] = drupal_map_assoc(range($min, $max));
|
||||||
|
$payment_form['cc_exp_year']['#default_value'] = $default;
|
||||||
|
|
||||||
|
$payment_form['cc_exp_month']['#type'] = 'hidden';
|
||||||
|
$payment_form['cc_exp_month']['#default_value'] = 1;
|
||||||
|
$payment_form['cc_exp_month']['#attributes']['id'] = 'edit-panes-payment-details-cc-exp-month';
|
||||||
|
|
||||||
// Add custom submit which will do saving away of token during submit.
|
// Add custom submit which will do saving away of token during submit.
|
||||||
$form['#submit'][] = 'uc_stripe_checkout_form_customsubmit';
|
$form['#submit'][] = 'uc_stripe_checkout_form_customsubmit';
|
||||||
|
|
||||||
@ -195,6 +247,11 @@ function uc_stripe_form_uc_cart_checkout_form_alter(&$form, &$form_state) {
|
|||||||
'#markup' => "<div id='uc_stripe_messages' class='messages error hidden'></div>",
|
'#markup' => "<div id='uc_stripe_messages' class='messages error hidden'></div>",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//Clear any previous card payment failures
|
||||||
|
if(isset($_SESSION['stripe']['payment_failed'])){
|
||||||
|
unset($_SESSION['stripe']['payment_failed']);
|
||||||
|
}
|
||||||
|
|
||||||
if (uc_credit_default_gateway() == 'uc_stripe') {
|
if (uc_credit_default_gateway() == 'uc_stripe') {
|
||||||
if (variable_get('uc_stripe_testmode', TRUE)) {
|
if (variable_get('uc_stripe_testmode', TRUE)) {
|
||||||
$form['panes']['testmode'] = array(
|
$form['panes']['testmode'] = array(
|
||||||
@ -226,6 +283,51 @@ function uc_stripe_uc_order_pane() {
|
|||||||
return $panes;
|
return $panes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_uc_checkout_pane to add checkout pane for stripe payment details
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function uc_stripe_uc_checkout_pane() {
|
||||||
|
$panes['payment-stripe'] = array(
|
||||||
|
'callback' => '_uc_stripe_payment_pane_callback',
|
||||||
|
'title' => t('Payment Information'),
|
||||||
|
'desc' => t("Accept stripe payment from customer."),
|
||||||
|
'weight' => 6,
|
||||||
|
'process' => FALSE,
|
||||||
|
'collapsible' => FALSE,
|
||||||
|
);
|
||||||
|
return $panes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements uc_checkout_pane_callback() specified in 'callback' of
|
||||||
|
* uc_stripe_uc_checkout_pane()
|
||||||
|
*
|
||||||
|
* Provides empty pane for stripe elements to be added
|
||||||
|
* @param $op
|
||||||
|
* @param $order
|
||||||
|
* @param $form
|
||||||
|
* @param $form_state
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function _uc_stripe_payment_pane_callback($op, $order, $form = NULL, &$form_state = NULL) {
|
||||||
|
// Create separate payment pane for stripe because the normal payment pane is refreshed many times
|
||||||
|
// by ajax, by country changes, etc.. Refreshing the payment section triggers Stripe Api's security feature
|
||||||
|
// and destroys the Stripe Element in the DOM.
|
||||||
|
// Emtpy values needed so that pane still appears.
|
||||||
|
switch ($op) {
|
||||||
|
case 'view':
|
||||||
|
$description = t('');
|
||||||
|
$contents['stripe_card_element'] = array(
|
||||||
|
'#markup' => '',
|
||||||
|
);
|
||||||
|
|
||||||
|
return array('description' => $description, 'contents' => $contents);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_uc_checkout_complete()
|
* Implements hook_uc_checkout_complete()
|
||||||
*
|
*
|
||||||
@ -237,12 +339,15 @@ function uc_stripe_uc_order_pane() {
|
|||||||
function uc_stripe_uc_checkout_complete($order, $account) {
|
function uc_stripe_uc_checkout_complete($order, $account) {
|
||||||
|
|
||||||
if ($order->payment_method == "credit") {
|
if ($order->payment_method == "credit") {
|
||||||
// Pull the stripe customer ID from the session.
|
// Pull the stripe payment method ID from the session.
|
||||||
// It got there in uc_stripe_checkout_form_customsubmit()
|
// It got there in uc_stripe_checkout_form_customsubmit()
|
||||||
$stripe_customer_id = $_SESSION['stripe']['customer_id'];
|
$stripe_payment_id = $_SESSION['stripe']['payment_method'];
|
||||||
|
|
||||||
|
$stripe_customer_id = $order->data['stripe_customer_id'];
|
||||||
|
|
||||||
$loaded_user = user_load($account->uid);
|
$loaded_user = user_load($account->uid);
|
||||||
user_save($loaded_user, array('data' => array('uc_stripe_customer_id' => $stripe_customer_id)));
|
user_save($loaded_user, array('data' => array('uc_stripe_customer_id' => $stripe_customer_id)));
|
||||||
|
user_save($loaded_user, array('data' => array('uc_stripe_payment_id' => $stripe_payment_id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,6 +414,15 @@ function uc_stripe_settings_form() {
|
|||||||
'#description' => t('Your Live Stripe API Key. Must be the "publishable" key, not the "secret" one.'),
|
'#description' => t('Your Live Stripe API Key. Must be the "publishable" key, not the "secret" one.'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$email_text = _uc_stripe_get_authentication_required_email_text();
|
||||||
|
|
||||||
|
$form['uc_stripe_settings']['uc_stripe_authentication_required_email'] = array(
|
||||||
|
'#type' => 'textarea',
|
||||||
|
'#title' => t('Email for Recurring payment authentication'),
|
||||||
|
'#default_value' => variable_get('uc_stripe_authentication_required_email', $email_text),
|
||||||
|
'#description' => t('If your site uses recurring payments, some transactions will require the customer to return to the site and authenticate before the subscrption payment can be processed.')
|
||||||
|
);
|
||||||
|
|
||||||
$form['uc_stripe_settings']['uc_stripe_testmode'] = array(
|
$form['uc_stripe_settings']['uc_stripe_testmode'] = array(
|
||||||
'#type' => 'checkbox',
|
'#type' => 'checkbox',
|
||||||
'#title' => t('Test mode'),
|
'#title' => t('Test mode'),
|
||||||
@ -330,7 +444,7 @@ function uc_stripe_settings_form() {
|
|||||||
'#default_value' => variable_get('uc_stripe_metadata_titles', FALSE),
|
'#default_value' => variable_get('uc_stripe_metadata_titles', FALSE),
|
||||||
);
|
);
|
||||||
|
|
||||||
$form['uc_stripe_settings']['uc_stripe_metadata_models'] = array(
|
$form['uc_stripe_settings']['uc_stripe_metadata_models'] = array(
|
||||||
'#type' => 'checkbox',
|
'#type' => 'checkbox',
|
||||||
'#title' => t('Metadata: Model'),
|
'#title' => t('Metadata: Model'),
|
||||||
'#description' => t('Include item model(s) (SKU(s)) in Stripe metadata.'),
|
'#description' => t('Include item model(s) (SKU(s)) in Stripe metadata.'),
|
||||||
@ -410,8 +524,8 @@ function _uc_stripe_validate_key($key) {
|
|||||||
*/
|
*/
|
||||||
function uc_stripe_checkout_form_customsubmit($form, &$form_state) {
|
function uc_stripe_checkout_form_customsubmit($form, &$form_state) {
|
||||||
// This submit may be entered on another payment type, so don't set session in that case.
|
// This submit may be entered on another payment type, so don't set session in that case.
|
||||||
if (!empty($form_state['values']['panes']['payment']['details']['stripe_token'])) {
|
if (!empty($form_state['values']['panes']['payment-stripe']['details']['stripe_payment_method'])) {
|
||||||
$_SESSION['stripe']['token'] = $form_state['values']['panes']['payment']['details']['stripe_token'];
|
$_SESSION['stripe']['payment_method'] = $form_state['values']['panes']['payment-stripe']['details']['stripe_payment_method'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,85 +571,6 @@ function uc_stripe_charge($order_id, $amount, $data) {
|
|||||||
// Format the amount in cents, which is what Stripe wants
|
// Format the amount in cents, which is what Stripe wants
|
||||||
$amount = uc_currency_format($amount, FALSE, FALSE, FALSE);
|
$amount = uc_currency_format($amount, FALSE, FALSE, FALSE);
|
||||||
|
|
||||||
$stripe_customer_id = FALSE;
|
|
||||||
|
|
||||||
// If the user running the order is not the order's owner
|
|
||||||
// (like if an admin is processing an order on someone's behalf)
|
|
||||||
// then load the customer ID from the user object.
|
|
||||||
// Otherwise, make a brand new customer each time a user checks out.
|
|
||||||
if ($user->uid != $order->uid) {
|
|
||||||
$stripe_customer_id = _uc_stripe_get_customer_id($order->uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Always Create a new customer in stripe for new orders
|
|
||||||
|
|
||||||
if (!$stripe_customer_id) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
// If the token is not in the user's session, we can't set up a new customer
|
|
||||||
if (empty($_SESSION['stripe']['token'])) {
|
|
||||||
throw new Exception('Token not found');
|
|
||||||
}
|
|
||||||
$stripe_token = $_SESSION['stripe']['token'];
|
|
||||||
|
|
||||||
$shipping_info = array();
|
|
||||||
if (!empty($order->delivery_postal_code)) {
|
|
||||||
$shipping_info = array(
|
|
||||||
'name' => @"{$order->delivery_first_name} {$order->delivery_last_name}",
|
|
||||||
'phone' => @$order->delivery_phone,
|
|
||||||
);
|
|
||||||
|
|
||||||
$delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
|
|
||||||
if ($delivery_country === FALSE) {
|
|
||||||
$delivery_country = array(0 => array('country_iso_code_2' => 'US'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$shipping_info['address'] = array(
|
|
||||||
'city' => @$order->delivery_city,
|
|
||||||
'country' => @$delivery_country[0]['country_iso_code_2'],
|
|
||||||
'line1' => @$order->delivery_street1,
|
|
||||||
'line2' => @$order->delivery_street2,
|
|
||||||
'postal_code' => @$order->delivery_postal_code,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$params = array(
|
|
||||||
"source" => $stripe_token,
|
|
||||||
'description' => "OrderID: {$order->order_id}",
|
|
||||||
'email' => "$order->primary_email"
|
|
||||||
);
|
|
||||||
if (!empty($shipping_info)) {
|
|
||||||
$params['shipping'] = $shipping_info;
|
|
||||||
}
|
|
||||||
//Create the customer in stripe
|
|
||||||
$customer = \Stripe\Customer::create($params);
|
|
||||||
|
|
||||||
// Store the customer ID in the session,
|
|
||||||
// We'll pick it up later to save it in the database since we might not have a $user object at this point anyway
|
|
||||||
$stripe_customer_id = $_SESSION['stripe']['customer_id'] = $customer->id;
|
|
||||||
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$result = array(
|
|
||||||
'success' => FALSE,
|
|
||||||
'comment' => $e->getCode(),
|
|
||||||
'message' => t("Stripe Customer Creation Failed for order !order: !message", array(
|
|
||||||
"!order" => $order_id,
|
|
||||||
"!message" => $e->getMessage()
|
|
||||||
)),
|
|
||||||
'uid' => $user->uid,
|
|
||||||
'order_id' => $order_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
uc_order_comment_save($order_id, $user->uid, $result['message'], 'admin');
|
|
||||||
|
|
||||||
watchdog('uc_stripe', 'Failed stripe customer creation: @message', array('@message' => $result['message']));
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Charge the stripe customer the amount in the order
|
// Charge the stripe customer the amount in the order
|
||||||
|
|
||||||
//--Handle transactions for $0
|
//--Handle transactions for $0
|
||||||
@ -553,47 +588,68 @@ function uc_stripe_charge($order_id, $amount, $data) {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Charge the customer
|
$stripe_customer_id = null;
|
||||||
|
|
||||||
|
if(key_exists('stripe_customer_id', $order->data)){
|
||||||
|
$stripe_customer_id = $order->data['stripe_customer_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rexamine Payment Intent and Record payment or failure to the customer
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
if(!key_exists('payment_intent_id', $order->data)){
|
||||||
|
throw new Exception('The payment Intent has failed.');
|
||||||
|
}
|
||||||
|
|
||||||
//Bail if there's no customer ID
|
//Bail if there's no customer ID
|
||||||
if (empty($stripe_customer_id)) {
|
if (empty($stripe_customer_id) || is_null($stripe_customer_id)) {
|
||||||
throw new Exception('No customer ID found');
|
throw new Exception('No customer ID found');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($order->products as $item){
|
//Bail if there's no payment method
|
||||||
$titles[] = $item->title;
|
if (empty($_SESSION['stripe']['payment_method'])) {
|
||||||
$models[] = $item->model;
|
throw new Exception('Token not found');
|
||||||
}
|
}
|
||||||
$metadata = array();
|
$stripe_payment_method_id = $_SESSION['stripe']['payment_method'];
|
||||||
|
|
||||||
if (!empty($models)) {
|
|
||||||
$metadata['models'] = implode(";", $models);
|
|
||||||
}
|
|
||||||
if (!empty($titles)) {
|
|
||||||
$metadata['titles'] = implode(";", $titles);
|
|
||||||
}
|
|
||||||
$params = array(
|
$params = array(
|
||||||
"amount" => $amount,
|
"amount" => $amount,
|
||||||
"currency" => strtolower($order->currency),
|
"currency" => strtolower($order->currency),
|
||||||
"customer" => $stripe_customer_id,
|
"customer" => $stripe_customer_id,
|
||||||
"description" => t("Order #@order_id", array("@order_id" => $order_id)),
|
"description" => t("Order #@order_id", array("@order_id" => $order_id)),
|
||||||
"metadata" => $metadata,
|
"metadata" => make_metadata($order),
|
||||||
|
"payment_method" => $stripe_payment_method_id,
|
||||||
|
"payment_method_types" => ['card'],
|
||||||
|
"confirm" => true,
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$intent_id = $order->data['payment_intent_id'];
|
||||||
|
|
||||||
if (!empty($shipping_info)) {
|
if (!empty($shipping_info)) {
|
||||||
$params['shipping'] = $shipping_info;
|
$params['shipping'] = $shipping_info;
|
||||||
|
\Stripe\PaymentIntent::update($intent_id, ['shipping' => $shipping_info]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// charge the Customer the amount in the order
|
// charge the Customer the amount in the order
|
||||||
$charge = \Stripe\Charge::create($params);
|
$payment_intent = \Stripe\PaymentIntent::retrieve($intent_id);
|
||||||
|
|
||||||
|
if ($payment_intent->status != 'succeeded') {
|
||||||
|
throw new Exception($payment_intent['last_payment_error']['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$charge_id = $payment_intent->charges->data[0]['id'];
|
||||||
|
|
||||||
$formatted_amount = $amount / 100;
|
$formatted_amount = $amount / 100;
|
||||||
$formatted_amount = number_format($formatted_amount, 2);
|
$formatted_amount = number_format($formatted_amount, 2);
|
||||||
|
|
||||||
|
// $payment_method = \Stripe\PaymentMethod::retrieve($payment_intent->payment_method);
|
||||||
|
// $payment_method->attach(['customer' => $stripe_customer_id]);
|
||||||
|
|
||||||
$result = array(
|
$result = array(
|
||||||
'success' => TRUE,
|
'success' => TRUE,
|
||||||
'message' => t('Payment of @amount processed successfully, Stripe transaction id @transaction_id.', array('@amount' => $formatted_amount, '@transaction_id' => $charge->id)),
|
'message' => t('Payment of @amount processed successfully, Stripe transaction id @transaction_id.', array('@amount' => $formatted_amount, '@transaction_id' => $charge_id)),
|
||||||
'comment' => t('Stripe transaction ID: @transaction_id', array('@transaction_id' => $charge->id)),
|
'comment' => t('Stripe transaction ID: @transaction_id', array('@transaction_id' => $charge_id)),
|
||||||
'uid' => $user->uid,
|
'uid' => $user->uid,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -616,6 +672,8 @@ function uc_stripe_charge($order_id, $amount, $data) {
|
|||||||
uc_order_comment_save($order_id, $user->uid, $result['message'], 'admin');
|
uc_order_comment_save($order_id, $user->uid, $result['message'], 'admin');
|
||||||
watchdog('uc_stripe', 'Stripe charge failed for order @order, message: @message', array('@order' => $order_id, '@message' => $result['message']));
|
watchdog('uc_stripe', 'Stripe charge failed for order @order, message: @message', array('@order' => $order_id, '@message' => $result['message']));
|
||||||
|
|
||||||
|
$_SESSION['stripe']['payment_failed'] = TRUE;
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,6 +691,8 @@ function uc_stripe_charge($order_id, $amount, $data) {
|
|||||||
|
|
||||||
watchdog('uc_stripe', 'Stripe gateway error for order @order_id', array('order_id' => $order_id));
|
watchdog('uc_stripe', 'Stripe gateway error for order @order_id', array('order_id' => $order_id));
|
||||||
|
|
||||||
|
$_SESSION['stripe']['payment_failed'] = TRUE;
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,33 +716,65 @@ function uc_stripe_renew($order, &$fee) {
|
|||||||
|
|
||||||
//Get the customer ID
|
//Get the customer ID
|
||||||
$stripe_customer_id = _uc_stripe_get_customer_id($order->uid);
|
$stripe_customer_id = _uc_stripe_get_customer_id($order->uid);
|
||||||
|
$stripe_payment_id = _uc_stripe_get_payment_id($order->uid);
|
||||||
|
|
||||||
if (empty($stripe_customer_id)) {
|
if (empty($stripe_customer_id)) {
|
||||||
throw new Exception('No stripe customer ID found');
|
throw new Exception('No stripe customer ID found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Create the charge
|
|
||||||
$amount = $fee->fee_amount;
|
$amount = $fee->fee_amount;
|
||||||
$amount = $amount * 100;
|
$amount = $amount * 100;
|
||||||
|
|
||||||
$charge = \Stripe\Charge::create(array(
|
//create intent Array
|
||||||
"amount" => $amount,
|
$intent_params = array(
|
||||||
"currency" => strtolower($order->currency),
|
'amount' => $amount,
|
||||||
"customer" => $stripe_customer_id
|
'currency' => strtolower($order->currency),
|
||||||
)
|
'payment_method_types' => ['card'],
|
||||||
|
'customer' => $stripe_customer_id,
|
||||||
|
'off_session' => true,
|
||||||
|
'confirm' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Payment methods added with Stripe PaymentIntent API will be saved to customer
|
||||||
|
// object in drupal. Payment cards saved with 2.x tokens will not have a value
|
||||||
|
// saved to customer object, but the payment Intent will still continue because
|
||||||
|
// Stipe will use default payment in those situations.
|
||||||
|
if($stripe_payment_id){
|
||||||
|
$intent_params['payment_method'] = $stripe_payment_id;
|
||||||
|
}
|
||||||
|
|
||||||
uc_payment_enter($order->order_id, $order->payment_method, $order->order_total, $fee->uid, $charge, "Success");
|
$payment_intent = \Stripe\PaymentIntent::create($intent_params);
|
||||||
|
|
||||||
|
uc_payment_enter($order->order_id, $order->payment_method, $order->order_total, $fee->uid, $payment_intent, "Success");
|
||||||
|
|
||||||
$formatted_amount = number_format($fee->fee_amount, 2);
|
$formatted_amount = number_format($fee->fee_amount, 2);
|
||||||
$message = t('Card renewal payment of @amount processed successfully.', array('@amount' => $formatted_amount));
|
$message = t('Card renewal payment of @amount processed successfully.', array('@amount' => $formatted_amount));
|
||||||
uc_order_comment_save($fee->order_id, $order->uid, $message, 'order', 'completed', FALSE);
|
uc_order_comment_save($fee->order_id, $order->uid, $message, 'order', 'completed', FALSE);
|
||||||
|
uc_order_comment_save($fee->order_id, $order->uid, $message, 'admin');
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (\Stripe\Error\Card $e) {
|
||||||
|
|
||||||
|
if ($e->getDeclineCode() === 'authentication_required') {
|
||||||
|
$NOT_COMPLETED = 0;
|
||||||
|
// Create and store hash so that we can prompt user to authenticate payment.
|
||||||
|
$hash = drupal_hmac_base64(REQUEST_TIME . $order->order_id, drupal_get_hash_salt() . $stripe_payment_id);
|
||||||
|
db_insert('uc_stripe_pending_auth')->fields(array(
|
||||||
|
'order_id' => $order->order_id,
|
||||||
|
'completed' => $NOT_COMPLETED,
|
||||||
|
'rfee_id' => $fee->rfid,
|
||||||
|
'hash' => $hash
|
||||||
|
))->execute();
|
||||||
|
|
||||||
|
// Prepare email to alert user that authentication is required.
|
||||||
|
$params['body'] = variable_get('uc_stripe_authentication_required_email', _uc_stripe_get_authentication_required_email_text());
|
||||||
|
$params['user'] = user_load($order->uid);
|
||||||
|
$params['hash'] = $hash;
|
||||||
|
drupal_mail('uc_stripe', 'authentication_required', $params['user']->mail,language_default(), $params);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
$result = array(
|
$result = array(
|
||||||
'success' => FALSE,
|
'success' => FALSE,
|
||||||
'comment' => $e->getCode(),
|
'comment' => $e->getCode(),
|
||||||
@ -692,10 +784,14 @@ function uc_stripe_renew($order, &$fee) {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
uc_order_comment_save($order->order_id, $order->uid, $result['message'], 'admin');
|
uc_order_comment_save($order->order_id, $order->uid, $result['message'], 'admin');
|
||||||
|
|
||||||
watchdog('uc_stripe', 'Renewal failed for order @order_id, code=@code, message: @message', array('@order_id' => $order->order_id, '@code' => $result['comment'], '@message' => $result['message']));
|
watchdog('uc_stripe', 'Renewal failed for order @order_id, code=@code, message: @message', array('@order_id' => $order->order_id, '@code' => $e->getCode(), '@message' => $e->getMessage()));
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
watchdog('uc_stripe', 'Renewal failed for order @order_id, code=@code, message: @message', array('@order_id' => $order->order_id, '@code' => $e->getCode(), '@message' => $e->getMessage()));
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,6 +855,13 @@ function _uc_stripe_prepare_api() {
|
|||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
watchdog('uc_stripe', 'Error setting the Stripe API Key. Payments will not be processed: %error', array('%error' => $e->getMessage()));
|
watchdog('uc_stripe', 'Error setting the Stripe API Key. Payments will not be processed: %error', array('%error' => $e->getMessage()));
|
||||||
}
|
}
|
||||||
|
try{
|
||||||
|
$module_info = system_get_info('module', "uc_stripe");
|
||||||
|
$uc_stripe_version = is_null($module_info['version']) ? 'dev-unknown' : $module_info['version'];
|
||||||
|
\Stripe\Stripe::setAppInfo("Drupal Ubercart Stripe", $uc_stripe_version, "https://www.drupal.org/project/uc_stripe");
|
||||||
|
} catch (Exception $e){
|
||||||
|
watchdog('uc_stripe', 'Error setting Stripe plugin information: %error', array('%error' => $e->getMessage()));
|
||||||
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,6 +892,20 @@ function _uc_stripe_get_customer_id($uid) {
|
|||||||
return $id;
|
return $id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Stripe payment id for a user
|
||||||
|
*
|
||||||
|
* @param $uid
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function _uc_stripe_get_payment_id($uid) {
|
||||||
|
|
||||||
|
$account = user_load($uid);
|
||||||
|
|
||||||
|
$id = !empty($account->data['uc_stripe_payment_id']) ? $account->data['uc_stripe_payment_id'] : FALSE;
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_theme_registry_alter() to make sure that we render
|
* Implements hook_theme_registry_alter() to make sure that we render
|
||||||
@ -808,13 +925,243 @@ function uc_stripe_theme_registry_alter(&$theme_registry) {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
function uc_stripe_uc_payment_method_credit_form($form) {
|
function uc_stripe_uc_payment_method_credit_form($form) {
|
||||||
$output = drupal_render($form['stripe_nojs_warning']);
|
|
||||||
$output .= drupal_render($form['config_error']);
|
$output .= drupal_render($form['config_error']);
|
||||||
$output .= theme('uc_payment_method_credit_form',$form);
|
$output .= theme('uc_payment_method_credit_form',$form);
|
||||||
$output .= drupal_render($form['stripe_token']);
|
$output .= drupal_render($form['stripe_payment_method']);
|
||||||
$output .= drupal_render($form['dummy_image_load']);
|
$output .= drupal_render($form['dummy_image_load']);
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to return the appropriate response after checking Stripe Payment Intent
|
||||||
|
* status
|
||||||
|
* @param Object $intent
|
||||||
|
* @return string response
|
||||||
|
*/
|
||||||
|
function _generatePaymentResponse($intent) {
|
||||||
|
|
||||||
|
if ($intent->status == 'requires_action' &&
|
||||||
|
$intent->next_action->type == 'use_stripe_sdk') {
|
||||||
|
# Tell the client to handle the action
|
||||||
|
$response = [
|
||||||
|
'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
|
||||||
|
$response = ['success' => true];
|
||||||
|
} else {
|
||||||
|
# Invalid status
|
||||||
|
http_response_code(500);
|
||||||
|
$response = ['error' => 'Invalid PaymentIntent status'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax page callback for callback uc_stripe/ajax/confirm_payment page
|
||||||
|
* This is used to send payment and intent status back to JS client
|
||||||
|
* @return string Json response
|
||||||
|
*/
|
||||||
|
function _uc_stripe_confirm_payment(){
|
||||||
|
|
||||||
|
global $user;
|
||||||
|
|
||||||
|
# retrieve json from POST body
|
||||||
|
$received_json = file_get_contents("php://input", TRUE);
|
||||||
|
$data = drupal_json_decode($received_json, TRUE);
|
||||||
|
|
||||||
|
$order_id = $data['order_id'];
|
||||||
|
$order = uc_order_load($order_id);
|
||||||
|
|
||||||
|
if (!_uc_stripe_prepare_api()) {
|
||||||
|
|
||||||
|
$message = 'Stripe API not found.';
|
||||||
|
|
||||||
|
watchdog('uc_stripe', 'Error in Stripe API: @message', array('@message' => $message));
|
||||||
|
return ['error' => $message];
|
||||||
|
}
|
||||||
|
// Format the amount in cents, which is what Stripe wants
|
||||||
|
$amount = uc_currency_format($order->order_total, FALSE, FALSE, FALSE);
|
||||||
|
|
||||||
|
|
||||||
|
$stripe_customer_id = False;
|
||||||
|
$order_has_stripe_id = key_exists('stripe_customer_id', $order->data) ? True : False;
|
||||||
|
|
||||||
|
// Check various places to get the stripe_customer_id. If not found we'll create
|
||||||
|
// a new stripe user.
|
||||||
|
if($order_has_stripe_id){
|
||||||
|
$stripe_customer_id = $order->data['stripe_customer_id'];
|
||||||
|
}
|
||||||
|
else if ($user->uid != $order->uid) {
|
||||||
|
$stripe_customer_id = _uc_stripe_get_customer_id($order->uid);
|
||||||
|
} else {
|
||||||
|
$stripe_customer_id = _uc_stripe_get_customer_id($user->uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the case where the stored customer_id is not a valid customer in Stripe
|
||||||
|
// then we'll need to create a new stripe customer. see #3071712
|
||||||
|
if($stripe_customer_id && !_uc_stripe_is_stripe_id_valid($stripe_customer_id)){
|
||||||
|
watchdog('uc_stripe', 'Stripe customer: @customer is not valid in this instance of Stripe. A new customer will be created.', array('@customer' => $stripe_customer_id));
|
||||||
|
$stripe_customer_id = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$intent = null;
|
||||||
|
try {
|
||||||
|
if (isset($data['payment_method_id'])) {
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'payment_method' => $data['payment_method_id'],
|
||||||
|
"description" => t("Order #@order_id", array("@order_id" => $order_id)),
|
||||||
|
"metadata" => make_metadata($order),
|
||||||
|
'amount' => $amount,
|
||||||
|
'currency' => strtolower($order->currency),
|
||||||
|
'confirmation_method' => 'manual',
|
||||||
|
'confirm' => true,
|
||||||
|
'setup_future_usage' => 'off_session',
|
||||||
|
'save_payment_method' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!$stripe_customer_id) {
|
||||||
|
$customer = _uc_stripe_create_stripe_customer($order, $data['payment_method_id']);
|
||||||
|
if(!$customer){
|
||||||
|
$message = 'Customer creation failed.';
|
||||||
|
$_SESSION['stripe']['payment_failed'] = TRUE;
|
||||||
|
return ['error' => $message];
|
||||||
|
}
|
||||||
|
$stripe_customer_id = $customer->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params['customer'] = $stripe_customer_id;
|
||||||
|
|
||||||
|
# Create the PaymentIntent
|
||||||
|
$intent = \Stripe\PaymentIntent::create($params);
|
||||||
|
|
||||||
|
if(!$order_has_stripe_id){
|
||||||
|
$order->data['stripe_customer_id'] = $stripe_customer_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$order->data['payment_intent_id'] = $intent->id;
|
||||||
|
uc_order_save($order);
|
||||||
|
}
|
||||||
|
if (isset($data['payment_intent_id'])) {
|
||||||
|
$intent = \Stripe\PaymentIntent::retrieve(
|
||||||
|
$data['payment_intent_id']
|
||||||
|
);
|
||||||
|
$intent->confirm();
|
||||||
|
|
||||||
|
$order->data['payment_intent_id'] = $data['payment_intent_id'];
|
||||||
|
uc_order_save($order);
|
||||||
|
}
|
||||||
|
return _generatePaymentResponse($intent);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
watchdog('uc_stripe', 'Payment could not be processed: @message', array('@message' => $e->getMessage()));
|
||||||
|
return ['error' => $e->getMessage()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _uc_stripe_create_stripe_customer($order, $payment_method_id = NULL){
|
||||||
|
|
||||||
|
$stripe_customer_id = FALSE;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// If the token is not in the user's session, we can't set up a new customer
|
||||||
|
|
||||||
|
$shipping_info = array();
|
||||||
|
if (!empty($order->delivery_postal_code)) {
|
||||||
|
$shipping_info = array(
|
||||||
|
'name' => @"{$order->delivery_first_name} {$order->delivery_last_name}",
|
||||||
|
'phone' => @$order->delivery_phone,
|
||||||
|
);
|
||||||
|
|
||||||
|
$delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
|
||||||
|
if ($delivery_country === FALSE) {
|
||||||
|
$delivery_country = array(0 => array('country_iso_code_2' => 'US'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$shipping_info['address'] = array(
|
||||||
|
'city' => @$order->delivery_city,
|
||||||
|
'country' => @$delivery_country[0]['country_iso_code_2'],
|
||||||
|
'line1' => @$order->delivery_street1,
|
||||||
|
'line2' => @$order->delivery_street2,
|
||||||
|
'postal_code' => @$order->delivery_postal_code,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'description' => "OrderID: {$order->order_id}",
|
||||||
|
"metadata" => make_metadata($order),
|
||||||
|
'email' => "$order->primary_email"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($shipping_info)) {
|
||||||
|
$params['shipping'] = $shipping_info;
|
||||||
|
}
|
||||||
|
//Create the customer in stripe
|
||||||
|
$customer = \Stripe\Customer::create($params);
|
||||||
|
|
||||||
|
return $customer;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$message = t("Stripe Customer Creation Failed for order !order: !message", array(
|
||||||
|
"!order" => $order_id,
|
||||||
|
"!message" => $e->getMessage()
|
||||||
|
));
|
||||||
|
|
||||||
|
uc_order_comment_save($order_id, $user->uid, $message, 'admin');
|
||||||
|
|
||||||
|
watchdog('uc_stripe', 'Failed stripe customer creation: @message', array('@message' => $message));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_metadata($order){
|
||||||
|
//Get item titles and models
|
||||||
|
foreach($order->products as $item){
|
||||||
|
$titles[] = $item->title;
|
||||||
|
$models[] = $item->model;
|
||||||
|
}
|
||||||
|
$metadata = array();
|
||||||
|
|
||||||
|
if (!empty($models) and variable_get('uc_stripe_metadata_models', FALSE)) {
|
||||||
|
$metadata['models'] = implode(";", $models);
|
||||||
|
}
|
||||||
|
if (!empty($titles) and variable_get('uc_stripe_metadata_titles', FALSE)) {
|
||||||
|
$metadata['titles'] = implode(";", $titles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param string $stripe_id
|
||||||
|
* @return boolean result - if stripe_id is valid based on stripe api customer call
|
||||||
|
*/
|
||||||
|
function _uc_stripe_is_stripe_id_valid($stripe_id){
|
||||||
|
try{
|
||||||
|
|
||||||
|
if (!_uc_stripe_prepare_api()) {
|
||||||
|
|
||||||
|
$message = 'Stripe API not found.';
|
||||||
|
|
||||||
|
watchdog('uc_stripe', 'Error in Stripe API: @message', array('@message' => $message));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$customer = \Stripe\Customer::retrieve($stripe_id);
|
||||||
|
|
||||||
|
// Count deleted stripe customers as invalid
|
||||||
|
return !$customer->deleted;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// IF customer is not found, an exception is thrown.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
149
uc_stripe.pages.inc
Normal file
149
uc_stripe.pages.inc
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_form().
|
||||||
|
*
|
||||||
|
* This form allows the user to authenticate in order for their recurring payment
|
||||||
|
* to be processed.
|
||||||
|
*/
|
||||||
|
function uc_stripe_authenticate_payment_form($form, &$form_state, $hash) {
|
||||||
|
$form = array();
|
||||||
|
|
||||||
|
$pending_order = db_select('uc_stripe_pending_auth', 'u')
|
||||||
|
->fields('u', array('order_id', 'completed', 'rfee_id'))
|
||||||
|
->condition('hash', $hash)
|
||||||
|
->execute()
|
||||||
|
->fetchObject();
|
||||||
|
|
||||||
|
|
||||||
|
if(!$pending_order){
|
||||||
|
$form['error'] = array(
|
||||||
|
'#markup' => t('Sorry, we could not verify your payment details. Please verify the link and try again. Contact support if the problem persists.'),
|
||||||
|
);
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
$order_id = $pending_order->order_id;
|
||||||
|
$completed = $pending_order->completed;
|
||||||
|
$rfee_id = $pending_order->rfee_id;
|
||||||
|
|
||||||
|
if ($completed) {
|
||||||
|
$form['error'] = array(
|
||||||
|
'#markup' => t('This payment has already been verified.'),
|
||||||
|
);
|
||||||
|
return $form;
|
||||||
|
};
|
||||||
|
|
||||||
|
$form['heading'] = array(
|
||||||
|
'#markup' => t('<p>Your financial institution has requested additional verification to process your scheduled payment.</p>'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['order_id'] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => $order_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['rfee_id'] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => $rfee_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['hash'] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => $hash,
|
||||||
|
);
|
||||||
|
|
||||||
|
$form['submit'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Verify Payment')
|
||||||
|
);
|
||||||
|
|
||||||
|
$order = uc_order_load($order_id);
|
||||||
|
$user = user_load($order->uid);
|
||||||
|
$payment_method_id = _uc_stripe_get_payment_id($user->uid);
|
||||||
|
$stripe_customer_id = _uc_stripe_get_customer_id($user->uid);
|
||||||
|
|
||||||
|
$order_id = $order_id;
|
||||||
|
$apikey = variable_get('uc_stripe_testmode', TRUE) ? check_plain(variable_get('uc_stripe_api_key_test_publishable', '')) : check_plain(variable_get('uc_stripe_api_key_live_publishable', ''));
|
||||||
|
$settings = array('apikey' => $apikey, 'methodId' => $payment_method_id, 'orderId' => $order_id);
|
||||||
|
|
||||||
|
//Attach Stripe v3 JS library and JS for processing payment
|
||||||
|
$form['#attached']['js']['https://js.stripe.com/v3/'] = array('type' => 'external');
|
||||||
|
$form['#attached']['js'][] = array('data' => array('uc_stripe' => $settings), 'type' => 'setting');
|
||||||
|
$form['#attached']['js'][] = drupal_get_path('module', 'uc_stripe') . '/js/uc_stripe_process_payment.js';
|
||||||
|
$form['#attached']['css'][] = drupal_get_path('module', 'uc_stripe') . '/css/uc_stripe.css';
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uc_stripe_authenticate_payment_form_submit($form, &$form_state){
|
||||||
|
|
||||||
|
$order_id = $form_state['values']['order_id'];
|
||||||
|
$rfee_id = $form_state['values']['rfee_id'];
|
||||||
|
$hash = $form_state['values']['hash'];
|
||||||
|
|
||||||
|
$order = uc_order_load($order_id);
|
||||||
|
$intent_id = $order->data['payment_intent_id'];
|
||||||
|
|
||||||
|
try{
|
||||||
|
_uc_stripe_prepare_api();
|
||||||
|
$payment_intent = \Stripe\PaymentIntent::retrieve($intent_id);
|
||||||
|
|
||||||
|
if ($payment_intent->status != 'succeeded') {
|
||||||
|
throw new Exception('Payment intent failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$charge_id = $payment_intent->charges->data[0]['id'];
|
||||||
|
$amount = uc_currency_format($order->order_total, FALSE, FALSE, FALSE);
|
||||||
|
|
||||||
|
$formatted_amount = $amount / 100;
|
||||||
|
$formatted_amount = number_format($formatted_amount, 2);
|
||||||
|
|
||||||
|
$message = t('Payment of @amount processed successfully, Stripe transaction id @transaction_id.', array('@amount' => $formatted_amount, '@transaction_id' => $charge_id));
|
||||||
|
$COMPLETED = 1;
|
||||||
|
|
||||||
|
//Set all orders matching the order id and fee id to completed. This is incase
|
||||||
|
// there were multiple attempts to process the subscription.
|
||||||
|
db_update('uc_stripe_pending_auth')
|
||||||
|
->fields(array(
|
||||||
|
'completed' => $COMPLETED,
|
||||||
|
))
|
||||||
|
->condition('order_id', $order_id)
|
||||||
|
->condition('rfee_id', $rfee_id)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$fee = uc_recurring_fee_user_load($rfee_id);
|
||||||
|
uc_payment_enter($order->order_id, $order->payment_method, $order->order_total, $fee->uid, $payment_intent, "Success");
|
||||||
|
|
||||||
|
// Since we have processed the payment here already, we'll temporarily change the fee
|
||||||
|
// handler to the the default uc_recurring fee handler that simply returns TRUE
|
||||||
|
// without any processing.
|
||||||
|
$fee->fee_handler = 'default';
|
||||||
|
$id = uc_recurring_renew($fee);
|
||||||
|
|
||||||
|
// We need to reset the fee handler for this order back to uc_stripe so that
|
||||||
|
// future subscriptions work.
|
||||||
|
$fee = uc_recurring_fee_user_load($fee->rfid);
|
||||||
|
$fee->fee_handler = 'uc_stripe';
|
||||||
|
uc_recurring_fee_user_save($fee);
|
||||||
|
|
||||||
|
uc_order_comment_save($order_id, $order->uid, $message, 'admin');
|
||||||
|
uc_order_comment_save($order_id, $order->uid, $message, 'order', 'completed', FALSE);
|
||||||
|
|
||||||
|
$form_state['redirect'] = '/';
|
||||||
|
drupal_set_message('You have successfully completed your payment');
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
|
||||||
|
$message = t("Stripe Charge Failed for order !order: !message", array(
|
||||||
|
"!order" => $order_id,
|
||||||
|
"!message" => $e->getMessage()
|
||||||
|
));
|
||||||
|
|
||||||
|
uc_order_comment_save($order_id, $order->uid, $message, 'admin');
|
||||||
|
watchdog('uc_stripe', 'Stripe charge failed for order @order, message: @message', array('@order' => $order_id, '@message' => $message));
|
||||||
|
$fail_message = variable_get('uc_credit_fail_message', t('We were unable to process your credit card payment. Please verify your details and try again. If the problem persists, contact us to complete your order.'));
|
||||||
|
drupal_set_message($fail_message, 'error');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user