WooCommerce Stripe Payment Gateway - Version 3.0.0

Version Description

  • First public WordPress.org release.
  • Refactor for WC 2.6 and above. Legacy support for 2.5.
  • Improved saved card handling using tokenization API in WooCommerce.

See changelog for all versions.

Download this release

Release Info

Developer mikejolley
Plugin Icon 128x128 WooCommerce Stripe Payment Gateway
Version 3.0.0
Comparing to
See all releases

Version 3.0.0

.github/CONTRIBUTING.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Create Bug Reports
2
+
3
+ If you find a bug or suggest enhancement, let us know by [opening a new issue](https://github.com/woothemes/woocommerce-gateway-stripe/issues/new).
4
+
5
+ ### Write and submit a patch
6
+
7
+ If you'd like to fix a bug, you can submit a Pull Request. If possible, raises an issue first and link the issue in your [commit message](https://help.github.com/articles/closing-issues-via-commit-messages/) or [PR's body](https://github.com/blog/1506-closing-issues-via-pull-requests).
8
+
9
+ When creating Pull Requests, remember:
10
+
11
+ - [Check In Early, Check In Often](http://blog.codinghorror.com/check-in-early-check-in-often/).
12
+ - Write [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
13
+ - Respect the [Best practices for WordPress development](http://jetpack.com/contribute/#practices).
14
+
.github/ISSUE_TEMPLATE.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- Thanks for contributing to this extension! Pick a clear title ("Order: Unable to refund") and proceed. -->
2
+
3
+ #### What I expected
4
+
5
+ <!-- What you expected when performing the steps -->
6
+
7
+ #### What happened instead
8
+
9
+ <!-- What actual results you got -->
10
+
11
+ #### Steps to reproduce the issue
12
+
13
+ <!-- Please add detailed steps to reproduce the issue. Make sure it's reproducible locally. Other extensions should be deactivated and standard theme is used when reproducing the issue locally -->
14
+
15
+ <!--
16
+ PLEASE NOTE
17
+ - These comments won't show up when you submit the issue.
18
+ - Everything is optional, but try to add as many details as possible.
19
+ - Screenshot worth a thousand words, use screenshots if possible.
20
+ - If requesting a new feature, explain why you'd like to see it added.
21
+ - Please apply appropriate labels on the issue
22
+ -->
23
+
24
+
25
+ -------------------
26
+
27
+ - [ ] Issue assigned to next milestone.
28
+ - [ ] Issue assigned a priority (will be assessed by maintainers).
.github/PULL_REQUEST_TEMPLATE.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ Fixes # .
2
+
3
+ #### Changes proposed in this Pull Request:
4
+ -
5
+
6
+ -------------------
7
+ - [ ] Make sure your changes respect [WordPress' coding standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/).
8
+ - [ ] Did you make changes, or create a **new .js file**? If **Gruntfile.js** exists in the repo, make sure to run `grunt`.
9
+
DEVELOPER.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DEVELOPER.md
2
+
3
+ ## Testing
4
+
5
+ * In wp-admin > WooCommerce > Settings > Checkout > Stripe, Enable Stripe, Enable Test Mode, Enable Stripe Checkout and Enable Payment via Saved Cards
6
+ * In wp-admin > WooCommerce > Settings > Checkout > Stripe, enter a Test Secret Key and a Test Publishable Key
7
+ * Enable at least one other payment gateway (e.g. Cheques)
8
+
9
+ * On the front side, place an item in your cart and proceed to Checkout
10
+ * Fill in all required fields in the Billing Details area
11
+ * Select Credit Card (Stripe) and "Use a new credit card"
12
+ * Click on Continue to payment
13
+ * Verify you get the stripe modal requesting card number, expiration and CVC
14
+ * Enter 4242 4242 4242 4242, 12/17, 123
15
+ * Leave Remember Me unchecked
16
+ * Click Confirm and Pay
17
+ * Verify the modal closes, the page dims for a bit, and then you are redirected to Order Received
18
+
19
+ * Repeat the above steps, but this time instead of "Use a new credit card" use a stored card
20
+ * Click on Continue to payment
21
+ * Verify the page dims for a bit and then you are redirected to Order Received
22
+
23
+ * Repeat the above steps, but this time clear the Billing Details (e.g. Name, etc)
24
+ * Choose a stored card in Stripe
25
+ * Click on Continue to payment
26
+ * Verify you get prompted to fill in required fields.
27
+ * Fill in the required fields
28
+ * Click on Continue to payment
29
+ * Verify the page dims for a bit and then you are redirected to Order Received
30
+
31
+ * Repeat the above steps, but this time choose the "Cheque Payment" gateway
32
+ * Click on Place Order
33
+ * Verify the page dims for a bit and then you are redirected to Order Received
34
+
35
+ * Repeat at least the "Use a new credit card" case on Chrome on an iPhone or iPad
36
+
37
+ * In wp-admin > WooCommerce > Settings > Checkout > Stripe, uncheck Enable Payment via Saved Cards
38
+ * On the front side, place an item in your cart and proceed to Checkout
39
+ * Fill in all required fields in the Billing Details area
40
+ * Select Credit Card (Stripe)
41
+ * Click on Continue to payment
42
+ * Verify you get the stripe modal requesting card number, expiration and CVC
43
+ * Enter 4242 4242 4242 4242, 12/17, 123
44
+ * Leave Remember Me unchecked
45
+ * Click Confirm and Pay
46
+ * Verify the modal closes, the page dims for a bit, and then you are redirected to Order Received
assets/images/bitcoin.png ADDED
Binary file
assets/images/bitcoin.svg ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
2
+ <svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="64" width="64" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
3
+ <g transform="translate(0.00630876,-0.00301984)">
4
+ <path fill="#f7931a" d="m63.033,39.744c-4.274,17.143-21.637,27.576-38.782,23.301-17.138-4.274-27.571-21.638-23.295-38.78,4.272-17.145,21.635-27.579,38.775-23.305,17.144,4.274,27.576,21.64,23.302,38.784z"/>
5
+ <path fill="#FFF" d="m46.103,27.444c0.637-4.258-2.605-6.547-7.038-8.074l1.438-5.768-3.511-0.875-1.4,5.616c-0.923-0.23-1.871-0.447-2.813-0.662l1.41-5.653-3.509-0.875-1.439,5.766c-0.764-0.174-1.514-0.346-2.242-0.527l0.004-0.018-4.842-1.209-0.934,3.75s2.605,0.597,2.55,0.634c1.422,0.355,1.679,1.296,1.636,2.042l-1.638,6.571c0.098,0.025,0.225,0.061,0.365,0.117-0.117-0.029-0.242-0.061-0.371-0.092l-2.296,9.205c-0.174,0.432-0.615,1.08-1.609,0.834,0.035,0.051-2.552-0.637-2.552-0.637l-1.743,4.019,4.569,1.139c0.85,0.213,1.683,0.436,2.503,0.646l-1.453,5.834,3.507,0.875,1.439-5.772c0.958,0.26,1.888,0.5,2.798,0.726l-1.434,5.745,3.511,0.875,1.453-5.823c5.987,1.133,10.489,0.676,12.384-4.739,1.527-4.36-0.076-6.875-3.226-8.515,2.294-0.529,4.022-2.038,4.483-5.155zm-8.022,11.249c-1.085,4.36-8.426,2.003-10.806,1.412l1.928-7.729c2.38,0.594,10.012,1.77,8.878,6.317zm1.086-11.312c-0.99,3.966-7.1,1.951-9.082,1.457l1.748-7.01c1.982,0.494,8.365,1.416,7.334,5.553z"/>
6
+ </g>
7
+ </svg>
assets/images/diners.png ADDED
Binary file
assets/js/stripe.js ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* global wc_stripe_params */
2
+ Stripe.setPublishableKey( wc_stripe_params.key );
3
+
4
+ jQuery( function( $ ) {
5
+
6
+ /* Open and close for legacy class */
7
+ jQuery( "form.checkout, form#order_review" ).on('change', 'input[name="wc-stripe-payment-token"]', function() {
8
+ if ( jQuery( '.stripe-legacy-payment-fields input[name="wc-stripe-payment-token"]:checked' ).val() == 'new' ) {
9
+ jQuery( '.stripe-legacy-payment-fields #stripe-payment-data' ).slideDown( 200 );
10
+ } else {
11
+ jQuery( '.stripe-legacy-payment-fields #stripe-payment-data' ).slideUp( 200 );
12
+ }
13
+ } );
14
+
15
+ /**
16
+ * Object to handle Stripe payment forms.
17
+ */
18
+ var wc_stripe_form = {
19
+
20
+ /**
21
+ * Initialize event handlers and UI state.
22
+ */
23
+ init: function( form ) {
24
+ this.form = form;
25
+
26
+ $( this.form )
27
+ .on(
28
+ 'submit checkout_place_order_stripe',
29
+ this.onSubmit
30
+ );
31
+
32
+ $( document )
33
+ .on(
34
+ 'change',
35
+ '#wc-stripe-cc-form :input',
36
+ this.onCCFormChange
37
+ )
38
+ .on(
39
+ 'stripeError',
40
+ this.onError
41
+ );
42
+ },
43
+
44
+ isStripeChosen: function() {
45
+ return $( '#payment_method_stripe' ).is( ':checked' ) && ( ! $( 'input[name="wc-stripe-payment-token"]:checked' ).length || 'new' === $( 'input[name="wc-stripe-payment-token"]:checked' ).val() );
46
+ },
47
+
48
+ hasToken: function() {
49
+ return 0 < $( 'input.stripe_token' ).length;
50
+ },
51
+
52
+ block: function() {
53
+ wc_stripe_form.form.block({
54
+ message: null,
55
+ overlayCSS: {
56
+ background: '#fff',
57
+ opacity: 0.6
58
+ }
59
+ });
60
+ },
61
+
62
+ unblock: function() {
63
+ wc_stripe_form.form.unblock();
64
+ },
65
+
66
+ onError: function( e, responseObject ) {
67
+ $( '.woocommerce-error, .stripe_token' ).remove();
68
+ $( '#stripe-card-number' ).closest( 'p' ).before( '<ul class="woocommerce_error woocommerce-error"><li>' + responseObject.response.error.message + '</li></ul>' );
69
+ wc_stripe_form.unblock();
70
+ },
71
+
72
+ onSubmit: function( e ) {
73
+ if ( wc_stripe_form.isStripeChosen() && ! wc_stripe_form.hasToken() ) {
74
+ e.preventDefault();
75
+ wc_stripe_form.block();
76
+
77
+ var card = $( '#stripe-card-number' ).val(),
78
+ cvc = $( '#stripe-card-cvc' ).val(),
79
+ expires = $( '#stripe-card-expiry' ).payment( 'cardExpiryVal' ),
80
+ first_name = $( '#billing_first_name' ).length ? $( '#billing_first_name' ).val() : wc_stripe_params.billing_first_name,
81
+ last_name = $( '#billing_last_name' ).length ? $( '#billing_last_name' ).val() : wc_stripe_params.billing_last_name,
82
+ address = {
83
+
84
+ },
85
+ data = {
86
+ number : card,
87
+ cvc : cvc,
88
+ exp_month: parseInt( expires['month'] ) || 0,
89
+ exp_year : parseInt( expires['year'] ) || 0,
90
+ name : first_name + ' ' + last_name
91
+
92
+ };
93
+
94
+ if ( jQuery('#billing_address_1').length > 0 ) {
95
+ data.address_line1 = $( '#billing_address_1' ).val();
96
+ data.address_line2 = $( '#billing_address_2' ).val();
97
+ data.address_state = $( '#billing_state' ).val();
98
+ data.address_city = $( '#billing_city' ).val();
99
+ data.address_zip = $( '#billing_postcode' ).val();
100
+ data.address_country = $( '#billing_country' ).val();
101
+ } else if ( data.address_line1 ) {
102
+ data.address_line1 = wc_stripe_params.billing_address_1;
103
+ data.address_line2 = wc_stripe_params.billing_address_2;
104
+ data.address_state = wc_stripe_params.billing_state;
105
+ data.address_city = wc_stripe_params.billing_city;
106
+ data.address_zip = wc_stripe_params.billing_postcode;
107
+ data.address_country = wc_stripe_params.billing_country;
108
+ }
109
+
110
+ Stripe.createToken( data, wc_stripe_form.onStripeReponse );
111
+
112
+ // Prevent form submitting
113
+ return false;
114
+ }
115
+ },
116
+
117
+ onCCFormChange: function() {
118
+ $( '.woocommerce-error, .stripe_token' ).remove();
119
+ },
120
+
121
+ onStripeReponse: function( status, response ) {
122
+ if ( response.error ) {
123
+ $( document ).trigger( 'stripeError', { response: response } );
124
+ } else {
125
+ // token contains id, last4, and card type
126
+ var token = response['id'];
127
+
128
+ // insert the token into the form so it gets submitted to the server
129
+ wc_stripe_form.form.append( "<input type='hidden' class='stripe_token' name='stripe_token' value='" + token + "'/>" );
130
+ wc_stripe_form.form.submit();
131
+ }
132
+ }
133
+ };
134
+
135
+ wc_stripe_form.init( $( "form.checkout, form#order_review, form#add_payment_method" ) );
136
+ } );
assets/js/stripe_checkout.js ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( function( $ ) {
2
+
3
+ /**
4
+ * Object to handle Stripe payment forms.
5
+ */
6
+ var wc_stripe_form = {
7
+
8
+ /**
9
+ * Initialize e handlers and UI state.
10
+ */
11
+ init: function( form ) {
12
+ this.form = form;
13
+ this.stripe_submit = false;
14
+
15
+ $( this.form )
16
+ // We need to bind directly to the click (and not checkout_place_order_stripe) to avoid popup blockers
17
+ // especially on mobile devices (like on Chrome for iOS) from blocking StripeCheckout.open from opening a tab
18
+ .on( 'click', '#place_order', this.onSubmit )
19
+
20
+ // WooCommerce lets us return a false on checkout_place_order_{gateway} to keep the form from submitting
21
+ .on( 'submit checkout_place_order_stripe' );
22
+ },
23
+
24
+ isStripeChosen: function() {
25
+ return $( '#payment_method_stripe' ).is( ':checked' ) && ( ! $( 'input[name="wc-stripe-payment-token"]:checked' ).length || 'new' === $( 'input[name="wc-stripe-payment-token"]:checked' ).val() );
26
+ },
27
+
28
+ isStripeModalNeeded: function( e ) {
29
+ var token = wc_stripe_form.form.find( 'input.stripe_token' );
30
+
31
+ // If this is a stripe submission (after modal) and token exists, allow submit.
32
+ if ( wc_stripe_form.stripe_submit && token ) {
33
+ return false;
34
+ }
35
+
36
+ // Don't affect submission if modal is not needed.
37
+ if ( ! wc_stripe_form.isStripeChosen() ) {
38
+ return false;
39
+ }
40
+
41
+ // Don't open modal if required fields are not complete
42
+ if ( $( 'input#terms' ).length === 1 && $( 'input#terms:checked' ).length === 0 ) {
43
+ return false;
44
+ }
45
+
46
+ if ( $( '#createaccount' ).is( ':checked' ) && $( '#account_password' ).length && $( '#account_password' ).val() === '' ) {
47
+ return false;
48
+ }
49
+
50
+ // check to see if we need to validate shipping address
51
+ if ( $( '#ship-to-different-address-checkbox' ).is( ':checked' ) ) {
52
+ $required_inputs = $( '.woocommerce-billing-fields .validate-required, .woocommerce-shipping-fields .validate-required' );
53
+ } else {
54
+ $required_inputs = $( '.woocommerce-billing-fields .validate-required' );
55
+ }
56
+
57
+ if ( $required_inputs.length ) {
58
+ var required_error = false;
59
+
60
+ $required_inputs.each( function() {
61
+ if ( $( this ).find( 'input.input-text, select' ).not( $( '#account_password, #account_username' ) ).val() === '' ) {
62
+ required_error = true;
63
+ }
64
+ });
65
+
66
+ if ( required_error ) {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ return true;
72
+ },
73
+
74
+ block: function() {
75
+ wc_stripe_form.form.block({
76
+ message: null,
77
+ overlayCSS: {
78
+ background: '#fff',
79
+ opacity: 0.6
80
+ }
81
+ });
82
+ },
83
+
84
+ unblock: function() {
85
+ wc_stripe_form.form.unblock();
86
+ },
87
+
88
+ onClose: function() {
89
+ wc_stripe_form.unblock();
90
+ },
91
+
92
+ onSubmit: function( e ) {
93
+ if ( wc_stripe_form.isStripeModalNeeded() ) {
94
+ e.preventDefault();
95
+
96
+ // Capture submittal and open stripecheckout
97
+ var $form = wc_stripe_form.form,
98
+ $data = $( '#stripe-payment-data' ),
99
+ token = $form.find( 'input.stripe_token' );
100
+
101
+ token.val( '' );
102
+
103
+ var token_action = function( res ) {
104
+ $form.find( 'input.stripe_token' ).remove();
105
+ $form.append( '<input type="hidden" class="stripe_token" name="stripe_token" value="' + res.id + '"/>' );
106
+ wc_stripe_form.stripe_submit = true;
107
+ $form.submit();
108
+ };
109
+
110
+ StripeCheckout.open({
111
+ key : wc_stripe_params.key,
112
+ address : false,
113
+ amount : $data.data( 'amount' ),
114
+ name : $data.data( 'name' ),
115
+ description : $data.data( 'description' ),
116
+ currency : $data.data( 'currency' ),
117
+ image : $data.data( 'image' ),
118
+ bitcoin : $data.data( 'bitcoin' ),
119
+ locale : $data.data( 'locale' ),
120
+ refund_mispayments: true, // for bitcoin payments let Stripe handle refunds if too little is paid
121
+ email : $( '#billing_email' ).val() || $data.data( 'email' ),
122
+ "panel-label" : $data.data( 'panel-label' ),
123
+ token : token_action,
124
+ closed : wc_stripe_form.onClose()
125
+ });
126
+
127
+ return false;
128
+ }
129
+
130
+ return true;
131
+ }
132
+ };
133
+
134
+ wc_stripe_form.init( $( "form.checkout, form#order_review, form#add_payment_method" ) );
135
+ } );
changelog.txt ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *** Changelog ***
2
+
3
+ = 3.0.0 =
4
+ * First public WordPress.org release.
5
+ * Refactor for WC 2.6 and above. Legacy support for 2.5.
6
+ * Improved saved card handling using tokenization API in WooCommerce.
7
+
8
+ = 2.6.12 - 2016.04.13 =
9
+ * Fix - When saved cards option is enabled with no cards on file, CC field was hidden.
10
+
11
+ = 2.6.11 - 2016.04.11 =
12
+ * Add - Option to set a default card in manage card section and detect previous card paid by subscription.
13
+ * Fix - Admin notice link when key is not added and when addons are present.
14
+
15
+ = 2.6.10 - 2016.03.16 =
16
+ * Tweak - Add logging mechanism. New 'Logging' option is added in Stripe gateway setting to enable logging.
17
+ * Fix - Allow language files to be located outside of plugin directory
18
+
19
+ = 2.6.9 - 2016.02.29 =
20
+ * Tweak - Allow mechanism to override Stripe JS error handler. See https://gist.github.com/gedex/240492f479c7443e4780
21
+ for an example to override error handler with simple alert.
22
+
23
+ = 2.6.8 - 2016.02.11 =
24
+ * Tweak - Include card brand in saved cards radio label class
25
+ * Tweak - Add action when deleting card
26
+ * Tweak - Add actions for add_card and add_customer
27
+ * Tweak - Add support for automatic localisation in Stripe Checkout modal
28
+ * Fix - Check for Stripe error code emptiness before returning the WP_Error
29
+
30
+ = 2.6.7 - 2015.12.17 =
31
+ * Fix is_available SSL check to also work properly on hosts that always serve HTTPS
32
+
33
+ = 2.6.6 - 2015.12.03 =
34
+ * Fix a JavaScript bug introduced in 2.6.4 that caused checkout with a saved card to fail
35
+
36
+ = 2.6.5 - 2015.12.02 =
37
+ * Do not require a card id when updating a subscription payment method
38
+
39
+ = 2.6.4 - 2015.11.20 =
40
+ * Fix a JavaScript bug that caused the Stripe Checkout popup to be blocked on Chrome for iOS
41
+
42
+ = 2.6.3 - 2015.11.12 =
43
+ * Add metadata to subscription payments in stripe dashboard to indicate whether it is the initial or a recurring payment
44
+
45
+ = 2.6.2 - 2015.11.06 =
46
+ * Fix bug that would cause multiple subscriptions to not be supported under certain circumstances
47
+
48
+ = 2.6.1 - 2015.09.15 =
49
+ * Unset source if not set during pre-order release payments.
50
+ * Store customer ID if not logged in for pre-order payments.
51
+
52
+ = 2.6.0 - 2015.09.02 =
53
+ * Subscriptions 2.0 support.
54
+
55
+ = 2.5.4 - 2015.08.11 =
56
+ * Tweak - Terms and conditions error styling when required
57
+ * Tweak - Account password error styling when required
58
+
59
+ = 2.5.3 - 2015.7.28 =
60
+ * Added - Filter to prevent Stripe from sending its own receipts "wc_stripe_send_stripe_receipt"
61
+
62
+ = 2.5.2 - 2015.07.19 =
63
+ * Fix - Removed deprecated add_error function
64
+ * Tweak - Improve error message when Stripe checkout function is used
65
+
66
+ = 2.5.1 - 2015.07.01 =
67
+ * Fix - Only send receipt_email when set.
68
+
69
+ = 2.5.0 - 2015.05.11 =
70
+ * Update to API version 2015-04-07
71
+ * Feature - Support authorize on subscriptions first payment.
72
+ * Tweak - Option labels.
73
+ * Tweak - Safe remote GET.
74
+ * Tweak - SSLVerify true.
75
+ * Tweak - Update card icons.
76
+ * Tweak - Pass receipt email.
77
+
78
+ = 2.4.3 - 2015.05.11 =
79
+ * Fix - fixed validation issue when account creation is not checked
80
+ * Update - Stripe checkout JS API v2
81
+
82
+ = 2.4.2 - 2015.03.23 =
83
+ * Fix - Create account password field was not being validated
84
+
85
+ = 2.4.1 - 2015.03.20 =
86
+ * Fix - Undefined JS error due to deprecated ajax_loader_url
87
+ * Fix - When using Stripe checkout JS, some form required fields were not validating
88
+
89
+ = 2.4.0 - 2015.02.20 =
90
+ * Added support for bitcoin currency
91
+
92
+ = 2.3.0 - 2015.01.31 =
93
+ * Added 'wc_stripe_description' filter to allow filtering of payment description.
94
+ * Added order_review handling for stripe checkout.
95
+ * Mark order as failed is Stripe API call fails
96
+ * Allow valid HTML in Stripe Description
97
+ * Fix settings link
98
+ * use get_order_currency() when generating payment args, rather than always using store currency.
99
+ * Fix fees where not logged correctly when using authorized first capture later
100
+ * Retry payment if customer_id is invalid.
101
+
102
+ = 2.2.8 - 2014.11.21 =
103
+ * Save card/customer id for regular orders.
104
+
105
+ = 2.2.7 - 2014.11.20 =
106
+ * Fixed all instances where order IDs were used instead of user IDs.
107
+ * Update orignal order card/customer ids for renewals.
108
+ * Add reasons to refunds.
109
+
110
+ = 2.2.6 - 2014.11.18 =
111
+ * Stripe card ID should be taken from the order, not the user.
112
+ * Fix order_meta_query.
113
+
114
+ = 2.2.5 - 2014.11.06 =
115
+ * Round totals to 2 decimals so when we multiply by 100 we're sure we've got an integer.
116
+
117
+ = 2.2.4 - 2014.10.01 =
118
+ * Fix card display for subscriptions.
119
+
120
+ = 2.2.3 - 2014.10.01 =
121
+ * Fixed textdomain name
122
+
123
+ = 2.2.2 - 2014.09.23 =
124
+ * Set API version to 2014-09-08.
125
+ * Fixed card display (type->brand).
126
+
127
+ = 2.2.1 - 2014.09.15 =
128
+ * Fix strict standards warning.
129
+
130
+ = 2.2.0 - 2014.09.01 =
131
+ * Replaced woocommerce_get_template (deprecated) with wc_get_template.
132
+ * Tweak refund support.
133
+ * Support for pre-orders.
134
+ * Fixed typo.
135
+
136
+ = 2.1.0 - 2014.08.06 =
137
+ * Associate stripe customers with wp users.
138
+ * Refactored saved card code.
139
+ * Use Stripe API to get and delete saved cards.
140
+ * Updated subscriptions integration for saved cards.
141
+ * WC 2.2 - Store transaction ID.
142
+ * WC 2.2 - Refund support.
143
+
144
+ = 2.0.4 - 2014.07.31 =
145
+ * Tweaked the stripe checkout submission method.
146
+
147
+ = 2.0.3 - 2014.07.25 =
148
+ * wc_stripe_manage_saved_cards_url filter.
149
+ * Zero decimal currency handling.
150
+ * Only open stripe model when required fields are completed.
151
+
152
+ = 2.0.2 - 2014.06.06 =
153
+ * Fix use of saved cards on subscriptions.
154
+
155
+ = 2.0.1 - 2014.05.29 =
156
+ * Fix ajax loading gif.
157
+ * Fix notices.
158
+ * Fix stray comma in stripe.js.
159
+ * Prompt user to accept terms before showing stripe checkout modal.
160
+
161
+ = 2.0.0 - 2014.05.21 =
162
+ * Added the WC credit_card_form - this extension now requires WC 2.1+
163
+ * Option to disable saved cards
164
+ * Refactored code base
165
+ * Fix jquery notices
166
+ * Fix settings page links
167
+ * woocommerce_stripe_request_body filter
168
+ * Store fees for subscriptions
169
+
170
+ = 1.8.6 - 2014.05.20 =
171
+ * correct SSl message
172
+ * decode get_bloginfo( 'name' ) for plain text display
173
+
174
+ = 1.8.5 - 2014.05.10 =
175
+ * Updated textdomains
176
+ * date_i18n
177
+ * Improve stripe checkout flow - pop up on the checkout button click
178
+
179
+ = 1.8.4 - 2014.04.01 =
180
+ * Fix updating credit card used for future subscription payments when paying for a failed subscription renewal order with a new credit card.
181
+
182
+ = 1.8.3 - 2014.02.13 =
183
+ * Fix fatal error for subscription payments of deleted products.
184
+
185
+ = 1.8.2 - 2014.02.06 =
186
+ * Fix notice on card delete
187
+
188
+ = 1.8.1 - 2014.01.28 =
189
+ * set default for $checked
190
+
191
+ = 1.8.0 - 2014.01.08 =
192
+ * Checked compatibility with 2013-12-03 API
193
+ * 2.1 compatibility
194
+ * Pre-filled email address when using stripe checkout
195
+
196
+ = 1.7.6 - 2013.12.02 =
197
+ * Fix card display
198
+
199
+ = 1.7.5 - 2013.11.27 =
200
+ * Show payment method for subscriptions on account page
201
+
202
+ = 1.7.4 - 2013.11.20 =
203
+ * Expand/close when using saved cards.
204
+ * Use balance_transaction to get and store fees
205
+
206
+ = 1.7.3 - 2013.11.01 =
207
+ * Default to saved card
208
+
209
+ = 1.7.2 - 2013.11.01 =
210
+ * Added missing global in update_failing_payment_method
211
+
212
+ = 1.7.1 - 2013.09.28 =
213
+ * Remove non-existant (yet) function
214
+
215
+ = 1.7.0 - 2013.09.25 =
216
+ * Different credit card image for US than for other countries + a filter.
217
+ * Support for upcoming version of subscriptions.
218
+ * Add new woocommerce_stripe_month_display filter
219
+
220
+ = 1.6.0 - 2013.09.02 =
221
+ * Option to define a Stripe Checkout Image
222
+ * Removed currency check due to beta rollout
223
+
224
+ = 1.5.14 - 2013.08.12 =
225
+ * New cards format for subscriptions class.
226
+
227
+ = 1.5.13 - 2013.07.24 =
228
+ * Updated customer response object handler to work with new cards format.
229
+ * Fixed delete card button
230
+
231
+ = 1.5.12 - 2013.07.24 =
232
+ * EUR support for Stripe Beta
233
+
234
+ = 1.5.11 - 2013.07.17 =
235
+ * Workaround for stripe error messages.
236
+
237
+ = 1.5.10 - 2013.06.28 =
238
+ * Store charge ID, fee in meta
239
+
240
+ = 1.5.9 - 2013.06.28 =
241
+ * Capture true default
242
+
243
+ = 1.5.8 - 2013.06.18 =
244
+ * Add currency to stripe checkout js
245
+ * Authorize-only mode. Captures payment when order is made processing.
246
+
247
+ = 1.5.7 - 2013.06.15 =
248
+ * Added 'capture' option should you wish to authorize only. Authorized orders are on-hold. Processed orders capture the charge automatically.
249
+
250
+ = 1.5.6 - 2013.06.03 =
251
+ * added data-currency to stripe-checkout
252
+
253
+ = 1.5.5 - 2013.04.26 =
254
+ * Allow card re-entry in stripe checkout after errors.
255
+
256
+ = 1.5.4 - 2013.04.19 =
257
+ * GBP fix
258
+
259
+ = 1.5.3 - 2013.04.15 =
260
+ * Support GBP currency code (For UK Beta)
261
+
262
+ = 1.5.2 - 2013.04.09 =
263
+ * Send billing city to stripe
264
+
265
+ = 1.5.1 - 2013.01.24 =
266
+ * Add support for changing a subscription's recurring amount
267
+
268
+ = 1.5.0 - 2013.01.18 =
269
+ * Supports Stripe Checkout https://stripe.com/docs/checkout
270
+
271
+ = 1.4.0 - 2013.01.18 =
272
+ * WC 2.0 Compat
273
+
274
+ = 1.3.5 - 2012.12.05 =
275
+ * Pass address fields to stripe.js on pay page.
276
+
277
+ = 1.3.4 - 2012.12.05 =
278
+ * Updater
279
+
280
+ = 1.3.3 - 2012.10.22 =
281
+ * Fix CAD check
282
+
283
+ = 1.3.2 - 2012.10.15 =
284
+ * Fixed bug causing settings to not show when using CAD
285
+
286
+ = 1.3.1 - 2012.10.11 =
287
+ * Add support for changing subscription next payment date
288
+ * Remove order meta from subscription renewal orders
289
+
290
+ = 1.3 - 2012.09.20 =
291
+ * Allowed canadian dollars - Stripe is beta testing support for Canada
292
+
293
+ = 1.2.1 - 2012.09.11 =
294
+ * Fix text mode SSL logic
295
+
296
+ = 1.2 - 2012.09.01 =
297
+ * SSL not required in TEST MODE
298
+ * Saved cards - store customer tokens and let users pay again using the same card
299
+ * Subscriptions use a single customer, rather than per-order
300
+ * Only load JS on checkout
301
+
302
+ = 1.1 - 2012.06.19 =
303
+ * Update woo updater
304
+ * Class name update
305
+ * Stripe JS for added security - you will need to re-enter keys and ensure you are using WooCommerce 1.5.8
306
+ * Subscriptions support (requires WC Subscriptions addon)
307
+
308
+ = 1.0 - 2011.12.08 =
309
+ * First Release
includes/class-wc-gateway-stripe-addons.php ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ /**
7
+ * WC_Gateway_Stripe_Addons class.
8
+ *
9
+ * @extends WC_Gateway_Stripe
10
+ */
11
+ class WC_Gateway_Stripe_Addons extends WC_Gateway_Stripe {
12
+
13
+ /**
14
+ * Constructor
15
+ */
16
+ public function __construct() {
17
+ parent::__construct();
18
+
19
+ if ( class_exists( 'WC_Subscriptions_Order' ) ) {
20
+ add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'scheduled_subscription_payment' ), 10, 2 );
21
+ add_action( 'wcs_resubscribe_order_created', array( $this, 'delete_resubscribe_meta' ), 10 );
22
+ add_action( 'wcs_renewal_order_created', array( $this, 'delete_renewal_meta' ), 10 );
23
+ add_action( 'woocommerce_subscription_failing_payment_method_updated_stripe', array( $this, 'update_failing_payment_method' ), 10, 2 );
24
+
25
+ // display the credit card used for a subscription in the "My Subscriptions" table
26
+ add_filter( 'woocommerce_my_subscriptions_payment_method', array( $this, 'maybe_render_subscription_payment_method' ), 10, 2 );
27
+
28
+ // allow store managers to manually set Stripe as the payment method on a subscription
29
+ add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 );
30
+ add_filter( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 );
31
+ }
32
+
33
+ if ( class_exists( 'WC_Pre_Orders_Order' ) ) {
34
+ add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this, 'process_pre_order_release_payment' ) );
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Is $order_id a subscription?
40
+ * @param int $order_id
41
+ * @return boolean
42
+ */
43
+ protected function is_subscription( $order_id ) {
44
+ return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
45
+ }
46
+
47
+ /**
48
+ * Is $order_id a pre-order?
49
+ * @param int $order_id
50
+ * @return boolean
51
+ */
52
+ protected function is_pre_order( $order_id ) {
53
+ return ( class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Order::order_contains_pre_order( $order_id ) );
54
+ }
55
+
56
+ /**
57
+ * Process the payment based on type.
58
+ * @param int $order_id
59
+ * @return array
60
+ */
61
+ public function process_payment( $order_id, $retry = true, $force_customer = false ) {
62
+ if ( $this->is_subscription( $order_id ) ) {
63
+ // Regular payment with force customer enabled
64
+ return parent::process_payment( $order_id, true, true );
65
+
66
+ } elseif ( $this->is_pre_order( $order_id ) ) {
67
+ return $this->process_pre_order( $order_id, $retry, $force_customer );
68
+
69
+ } else {
70
+ return parent::process_payment( $order_id, $retry, $force_customer );
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Updates other subscription sources.
76
+ */
77
+ protected function save_source( $order, $source ) {
78
+ parent::save_source( $order, $source );
79
+
80
+ // Also store it on the subscriptions being purchased or paid for in the order
81
+ if ( wcs_order_contains_subscription( $order->id ) ) {
82
+ $subscriptions = wcs_get_subscriptions_for_order( $order->id );
83
+ } elseif ( wcs_order_contains_renewal( $order->id ) ) {
84
+ $subscriptions = wcs_get_subscriptions_for_renewal_order( $order->id );
85
+ } else {
86
+ $subscriptions = array();
87
+ }
88
+
89
+ foreach( $subscriptions as $subscription ) {
90
+ update_post_meta( $subscription->id, '_stripe_customer_id', $source->customer );
91
+ update_post_meta( $subscription->id, '_stripe_card_id', $source->source );
92
+ }
93
+ }
94
+
95
+ /**
96
+ * process_subscription_payment function.
97
+ * @param mixed $order
98
+ * @param int $amount (default: 0)
99
+ * @param string $stripe_token (default: '')
100
+ * @param bool initial_payment
101
+ */
102
+ public function process_subscription_payment( $order = '', $amount = 0 ) {
103
+ if ( $amount * 100 < 50 ) {
104
+ return new WP_Error( 'stripe_error', __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce-gateway-stripe' ) );
105
+ }
106
+
107
+ // Get source from order
108
+ $source = $this->get_order_source( $order );
109
+
110
+ // If no order source was defined, use user source instead.
111
+ if ( ! $source->customer ) {
112
+ $source = $this->get_source( $order->customer_user );
113
+ }
114
+
115
+ // Or fail :(
116
+ if ( ! $source->customer ) {
117
+ return new WP_Error( 'stripe_error', __( 'Customer not found', 'woocommerce-gateway-stripe' ) );
118
+ }
119
+
120
+ WC_Stripe::log( "Info: Begin processing subscriotion payment for order {$order->id} for the amount of {$amount}" );
121
+
122
+ // Make the request
123
+ $request = $this->generate_payment_request( $order, $source );
124
+ $request['capture'] = 'true';
125
+ $request['amount'] = $this->get_stripe_amount( $amount, $request['currency'] );
126
+ $request['metadata'] = array(
127
+ 'payment_type' => 'recurring'
128
+ );
129
+ $response = WC_Stripe_API::request( $request );
130
+
131
+ // Process valid response
132
+ if ( ! is_wp_error( $response ) ) {
133
+ $this->process_response( $response, $order );
134
+ }
135
+
136
+ return $response;
137
+ }
138
+
139
+ /**
140
+ * Process the pre-order
141
+ * @param int $order_id
142
+ * @return array
143
+ */
144
+ public function process_pre_order( $order_id, $retry, $force_customer ) {
145
+ if ( WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) ) {
146
+ try {
147
+ $order = wc_get_order( $order_id );
148
+
149
+ if ( $order->get_total() * 100 < 50 ) {
150
+ throw new Exception( __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce-gateway-stripe' ) );
151
+ }
152
+
153
+ $source = $this->get_source( get_current_user_id(), true );
154
+
155
+ // We need a source on file to continue.
156
+ if ( empty( $source->customer ) || empty( $source->source ) ) {
157
+ throw new Exception( __( 'Unable to store payment details. Please try again.', 'woocommerce-gateway-stripe' ) );
158
+ }
159
+
160
+ // Store source to order meta
161
+ $this->save_source( $order, $source );
162
+
163
+ // Reduce stock levels
164
+ $order->reduce_order_stock();
165
+
166
+ // Remove cart
167
+ WC()->cart->empty_cart();
168
+
169
+ // Is pre ordered!
170
+ WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
171
+
172
+ // Return thank you page redirect
173
+ return array(
174
+ 'result' => 'success',
175
+ 'redirect' => $this->get_return_url( $order )
176
+ );
177
+ } catch ( Exception $e ) {
178
+ wc_add_notice( $e->getMessage(), 'error' );
179
+ return;
180
+ }
181
+ } else {
182
+ return parent::process_payment( $order_id, $retry, $force_customer );
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Process a pre-order payment when the pre-order is released
188
+ * @param WC_Order $order
189
+ * @return void
190
+ */
191
+ public function process_pre_order_release_payment( $order ) {
192
+ try {
193
+ // Define some callbacks if the first attempt fails.
194
+ $retry_callbacks = array(
195
+ 'remove_order_source_before_retry',
196
+ 'remove_order_customer_before_retry',
197
+ );
198
+
199
+ while ( 1 ) {
200
+ $source = $this->get_order_source( $order );
201
+ $response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
202
+
203
+ if ( is_wp_error( $response ) ) {
204
+ if ( 0 === sizeof( $retry_callbacks ) ) {
205
+ throw new Exception( $response->get_error_message() );
206
+ } else {
207
+ $retry_callback = array_shift( $retry_callbacks );
208
+ call_user_func( array( $this, $retry_callback ), $order );
209
+ }
210
+ } else {
211
+ // Successful
212
+ $this->process_response( $response, $order );
213
+ break;
214
+ }
215
+ }
216
+
217
+ } catch ( Exception $e ) {
218
+ $order_note = sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $e->getMessage() );
219
+
220
+ // Mark order as failed if not already set,
221
+ // otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times
222
+ if ( ! $order->has_status( 'failed' ) ) {
223
+ $order->update_status( 'failed', $order_note );
224
+ } else {
225
+ $order->add_order_note( $order_note );
226
+ }
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Don't transfer Stripe customer/token meta to resubscribe orders.
232
+ * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
233
+ */
234
+ public function delete_resubscribe_meta( $resubscribe_order ) {
235
+ delete_post_meta( $resubscribe_order->id, '_stripe_customer_id' );
236
+ delete_post_meta( $resubscribe_order->id, '_stripe_card_id' );
237
+ $this->delete_renewal_meta( $resubscribe_order );
238
+ }
239
+
240
+ /**
241
+ * Don't transfer Stripe fee/ID meta to renewal orders.
242
+ * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
243
+ */
244
+ public function delete_renewal_meta( $renewal_order ) {
245
+ delete_post_meta( $renewal_order->id, 'Stripe Fee' );
246
+ delete_post_meta( $renewal_order->id, 'Net Revenue From Stripe' );
247
+ delete_post_meta( $renewal_order->id, 'Stripe Payment ID' );
248
+ return $renewal_order;
249
+ }
250
+
251
+ /**
252
+ * scheduled_subscription_payment function.
253
+ *
254
+ * @param $amount_to_charge float The amount to charge.
255
+ * @param $renewal_order WC_Order A WC_Order object created to record the renewal payment.
256
+ */
257
+ public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
258
+ // Define some callbacks if the first attempt fails.
259
+ $retry_callbacks = array(
260
+ 'remove_order_source_before_retry',
261
+ 'remove_order_customer_before_retry',
262
+ );
263
+
264
+ while ( 1 ) {
265
+ $response = $this->process_subscription_payment( $renewal_order, $amount_to_charge );
266
+
267
+ if ( is_wp_error( $response ) ) {
268
+ if ( 0 === sizeof( $retry_callbacks ) ) {
269
+ $renewal_order->update_status( 'failed', sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
270
+ break;
271
+ } else {
272
+ $retry_callback = array_shift( $retry_callbacks );
273
+ call_user_func( array( $this, $retry_callback ), $renewal_order );
274
+ }
275
+ } else {
276
+ // Successful
277
+ break;
278
+ }
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Remove order meta
284
+ * @param object $order
285
+ */
286
+ public function remove_order_source_before_retry( $order ) {
287
+ delete_post_meta( $order->id, '_stripe_card_id' );
288
+ }
289
+
290
+ /**
291
+ * Remove order meta
292
+ * @param object $order
293
+ */
294
+ public function remove_order_customer_before_retry( $order ) {
295
+ delete_post_meta( $order->id, '_stripe_customer_id' );
296
+ }
297
+
298
+ /**
299
+ * Update the customer_id for a subscription after using Stripe to complete a payment to make up for
300
+ * an automatic renewal payment which previously failed.
301
+ *
302
+ * @access public
303
+ * @param WC_Subscription $subscription The subscription for which the failing payment method relates.
304
+ * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
305
+ * @return void
306
+ */
307
+ public function update_failing_payment_method( $subscription, $renewal_order ) {
308
+ update_post_meta( $subscription->id, '_stripe_customer_id', $renewal_order->stripe_customer_id );
309
+ update_post_meta( $subscription->id, '_stripe_card_id', $renewal_order->stripe_card_id );
310
+ }
311
+
312
+ /**
313
+ * Include the payment meta data required to process automatic recurring payments so that store managers can
314
+ * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
315
+ *
316
+ * @since 2.5
317
+ * @param array $payment_meta associative array of meta data required for automatic payments
318
+ * @param WC_Subscription $subscription An instance of a subscription object
319
+ * @return array
320
+ */
321
+ public function add_subscription_payment_meta( $payment_meta, $subscription ) {
322
+ $payment_meta[ $this->id ] = array(
323
+ 'post_meta' => array(
324
+ '_stripe_customer_id' => array(
325
+ 'value' => get_post_meta( $subscription->id, '_stripe_customer_id', true ),
326
+ 'label' => 'Stripe Customer ID',
327
+ ),
328
+ '_stripe_card_id' => array(
329
+ 'value' => get_post_meta( $subscription->id, '_stripe_card_id', true ),
330
+ 'label' => 'Stripe Card ID',
331
+ ),
332
+ ),
333
+ );
334
+ return $payment_meta;
335
+ }
336
+
337
+ /**
338
+ * Validate the payment meta data required to process automatic recurring payments so that store managers can
339
+ * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
340
+ *
341
+ * @since 2.5
342
+ * @param string $payment_method_id The ID of the payment method to validate
343
+ * @param array $payment_meta associative array of meta data required for automatic payments
344
+ * @return array
345
+ */
346
+ public function validate_subscription_payment_meta( $payment_method_id, $payment_meta ) {
347
+ if ( $this->id === $payment_method_id ) {
348
+
349
+ if ( ! isset( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) ) {
350
+ throw new Exception( 'A "_stripe_customer_id" value is required.' );
351
+ } elseif ( 0 !== strpos( $payment_meta['post_meta']['_stripe_customer_id']['value'], 'cus_' ) ) {
352
+ throw new Exception( 'Invalid customer ID. A valid "_stripe_customer_id" must begin with "cus_".' );
353
+ }
354
+
355
+ if ( ! empty( $payment_meta['post_meta']['_stripe_card_id']['value'] ) && 0 !== strpos( $payment_meta['post_meta']['_stripe_card_id']['value'], 'card_' ) ) {
356
+ throw new Exception( 'Invalid card ID. A valid "_stripe_card_id" must begin with "card_".' );
357
+ }
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Render the payment method used for a subscription in the "My Subscriptions" table
363
+ *
364
+ * @since 1.7.5
365
+ * @param string $payment_method_to_display the default payment method text to display
366
+ * @param WC_Subscription $subscription the subscription details
367
+ * @return string the subscription payment method
368
+ */
369
+ public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
370
+ // bail for other payment methods
371
+ if ( $this->id !== $subscription->payment_method || ! $subscription->customer_user ) {
372
+ return $payment_method_to_display;
373
+ }
374
+
375
+ $stripe_customer = new WC_Stripe_Customer();
376
+ $stripe_customer_id = get_post_meta( $subscription->id, '_stripe_customer_id', true );
377
+ $stripe_card_id = get_post_meta( $subscription->id, '_stripe_card_id', true );
378
+
379
+ // If we couldn't find a Stripe customer linked to the subscription, fallback to the user meta data.
380
+ if ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) {
381
+ $user_id = $subscription->customer_user;
382
+ $stripe_customer_id = get_user_meta( $user_id, '_stripe_customer_id', true );
383
+ $stripe_card_id = get_user_meta( $user_id, '_stripe_card_id', true );
384
+ }
385
+
386
+ // If we couldn't find a Stripe customer linked to the account, fallback to the order meta data.
387
+ if ( ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) && false !== $subscription->order ) {
388
+ $stripe_customer_id = get_post_meta( $subscription->order->id, '_stripe_customer_id', true );
389
+ $stripe_card_id = get_post_meta( $subscription->order->id, '_stripe_card_id', true );
390
+ }
391
+
392
+ $stripe_customer->set_id( $stripe_customer_id );
393
+ $cards = $stripe_customer->get_cards();
394
+
395
+ if ( $cards ) {
396
+ $found_card = false;
397
+ foreach ( $cards as $card ) {
398
+ if ( $card->id === $stripe_card_id ) {
399
+ $found_card = true;
400
+ $payment_method_to_display = sprintf( __( 'Via %s card ending in %s', 'woocommerce-gateway-stripe' ), ( isset( $card->type ) ? $card->type : $card->brand ), $card->last4 );
401
+ break;
402
+ }
403
+ }
404
+ if ( ! $found_card ) {
405
+ $payment_method_to_display = sprintf( __( 'Via %s card ending in %s', 'woocommerce-gateway-stripe' ), ( isset( $cards[0]->type ) ? $cards[0]->type : $cards[0]->brand ), $cards[0]->last4 );
406
+ }
407
+ }
408
+
409
+ return $payment_method_to_display;
410
+ }
411
+ }
includes/class-wc-gateway-stripe.php ADDED
@@ -0,0 +1,570 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ /**
7
+ * WC_Gateway_Stripe class.
8
+ *
9
+ * @extends WC_Payment_Gateway
10
+ */
11
+ class WC_Gateway_Stripe extends WC_Payment_Gateway_CC {
12
+
13
+ /**
14
+ * Constructor
15
+ */
16
+ public function __construct() {
17
+ $this->id = 'stripe';
18
+ $this->method_title = __( 'Stripe', 'woocommerce-gateway-stripe' );
19
+ $this->method_description = __( 'Stripe works by adding credit card fields on the checkout and then sending the details to Stripe for verification.', 'woocommerce-gateway-stripe' );
20
+ $this->has_fields = true;
21
+ $this->view_transaction_url = 'https://dashboard.stripe.com/payments/%s';
22
+ $this->supports = array(
23
+ 'subscriptions',
24
+ 'products',
25
+ 'refunds',
26
+ 'subscription_cancellation',
27
+ 'subscription_reactivation',
28
+ 'subscription_suspension',
29
+ 'subscription_amount_changes',
30
+ 'subscription_payment_method_change', // Subs 1.n compatibility
31
+ 'subscription_payment_method_change_customer',
32
+ 'subscription_payment_method_change_admin',
33
+ 'subscription_date_changes',
34
+ 'multiple_subscriptions',
35
+ 'pre-orders',
36
+ 'tokenization',
37
+ );
38
+
39
+ // Load the form fields
40
+ $this->init_form_fields();
41
+
42
+ // Load the settings.
43
+ $this->init_settings();
44
+
45
+ // Get setting values.
46
+ $this->title = $this->get_option( 'title' );
47
+ $this->description = $this->get_option( 'description' );
48
+ $this->enabled = $this->get_option( 'enabled' );
49
+ $this->testmode = 'yes' === $this->get_option( 'testmode' );
50
+ $this->capture = 'yes' === $this->get_option( 'capture', 'yes' );
51
+ $this->stripe_checkout = 'yes' === $this->get_option( 'stripe_checkout' );
52
+ $this->stripe_checkout_locale = $this->get_option( 'stripe_checkout_locale' );
53
+ $this->stripe_checkout_image = $this->get_option( 'stripe_checkout_image', '' );
54
+ $this->saved_cards = 'yes' === $this->get_option( 'saved_cards' );
55
+ $this->secret_key = $this->testmode ? $this->get_option( 'test_secret_key' ) : $this->get_option( 'secret_key' );
56
+ $this->publishable_key = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
57
+ $this->bitcoin = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' );
58
+ $this->logging = 'yes' === $this->get_option( 'logging' );
59
+
60
+ if ( $this->stripe_checkout ) {
61
+ $this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' );
62
+ }
63
+
64
+ if ( $this->testmode ) {
65
+ $this->description .= ' ' . sprintf( __( 'TEST MODE ENABLED. In test mode, you can use the card number 4242424242424242 with any CVC and a valid expiration date or check the documentation "<a href="%s">Testing Stripe</a>" for more card numbers.', 'woocommerce-gateway-stripe' ), 'https://stripe.com/docs/testing' );
66
+ $this->description = trim( $this->description );
67
+ }
68
+
69
+ WC_Stripe_API::set_secret_key( $this->secret_key );
70
+
71
+ // Hooks
72
+ add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
73
+ add_action( 'admin_notices', array( $this, 'admin_notices' ) );
74
+ add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
75
+ }
76
+
77
+ /**
78
+ * get_icon function.
79
+ *
80
+ * @access public
81
+ * @return string
82
+ */
83
+ public function get_icon() {
84
+ $ext = version_compare( WC()->version, '2.6', '>=' ) ? '.svg' : '.png';
85
+ $style = version_compare( WC()->version, '2.6', '>=' ) ? 'style="margin-left: 0.3em"' : '';
86
+
87
+ $icon = '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/visa' . $ext ) . '" alt="Visa" width="32" ' . $style . ' />';
88
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/mastercard' . $ext ) . '" alt="Mastercard" width="32" ' . $style . ' />';
89
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/amex' . $ext ) . '" alt="Amex" width="32" ' . $style . ' />';
90
+
91
+ if ( 'USD' === get_woocommerce_currency() ) {
92
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/discover' . $ext ) . '" alt="Discover" width="32" ' . $style . ' />';
93
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/jcb' . $ext ) . '" alt="JCB" width="32" ' . $style . ' />';
94
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/diners' . $ext ) . '" alt="Diners" width="32" ' . $style . ' />';
95
+ }
96
+
97
+ if ( $this->bitcoin ) {
98
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( plugins_url( '/assets/images/bitcoin' . $ext, WC_STRIPE_MAIN_FILE ) ) . '" alt="Bitcoin" width="32" ' . $style . ' />';
99
+ }
100
+
101
+ return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id );
102
+ }
103
+
104
+ /**
105
+ * Get Stripe amount to pay
106
+ * @return float
107
+ */
108
+ public function get_stripe_amount( $total, $currency = '' ) {
109
+ if ( ! $currency ) {
110
+ $currency = get_woocommerce_currency();
111
+ }
112
+ switch ( strtoupper( $currency ) ) {
113
+ // Zero decimal currencies
114
+ case 'BIF' :
115
+ case 'CLP' :
116
+ case 'DJF' :
117
+ case 'GNF' :
118
+ case 'JPY' :
119
+ case 'KMF' :
120
+ case 'KRW' :
121
+ case 'MGA' :
122
+ case 'PYG' :
123
+ case 'RWF' :
124
+ case 'VND' :
125
+ case 'VUV' :
126
+ case 'XAF' :
127
+ case 'XOF' :
128
+ case 'XPF' :
129
+ $total = absint( $total );
130
+ break;
131
+ default :
132
+ $total = round( $total, 2 ) * 100; // In cents
133
+ break;
134
+ }
135
+ return $total;
136
+ }
137
+
138
+ /**
139
+ * Check if SSL is enabled and notify the user
140
+ */
141
+ public function admin_notices() {
142
+ if ( 'no' === $this->enabled ) {
143
+ return;
144
+ }
145
+
146
+ // Show message if enabled and FORCE SSL is disabled and WordpressHTTPS plugin is not detected
147
+ if ( ( function_exists( 'wc_site_is_https' ) && ! wc_site_is_https() ) && ( 'no' === get_option( 'woocommerce_force_ssl_checkout' ) && ! class_exists( 'WordPressHTTPS' ) ) ) {
148
+ echo '<div class="error"><p>' . sprintf( __( 'Stripe is enabled, but the <a href="%s">force SSL option</a> is disabled; your checkout may not be secure! Please enable SSL and ensure your server has a valid SSL certificate - Stripe will only work in test mode.', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout' ) ) . '</p></div>';
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Check if this gateway is enabled
154
+ */
155
+ public function is_available() {
156
+ if ( 'yes' === $this->enabled ) {
157
+ if ( ! $this->testmode && is_checkout() && ! is_ssl() ) {
158
+ return false;
159
+ }
160
+ if ( ! $this->secret_key || ! $this->publishable_key ) {
161
+ return false;
162
+ }
163
+ return true;
164
+ }
165
+ return false;
166
+ }
167
+
168
+ /**
169
+ * Initialise Gateway Settings Form Fields
170
+ */
171
+ public function init_form_fields() {
172
+ $this->form_fields = include( 'settings-stripe.php' );
173
+
174
+ wc_enqueue_js( "
175
+ jQuery( function( $ ) {
176
+ $( '#woocommerce_stripe_stripe_checkout' ).change(function(){
177
+ if ( $( this ).is( ':checked' ) ) {
178
+ $( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).show();
179
+ } else {
180
+ $( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).hide();
181
+ }
182
+ }).change();
183
+ });
184
+ " );
185
+ }
186
+
187
+ /**
188
+ * Payment form on checkout page
189
+ */
190
+ public function payment_fields() {
191
+ $user = wp_get_current_user();
192
+ $display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards && $user->ID;
193
+
194
+ if ( $user->ID ) {
195
+ $user_email = get_user_meta( $user->ID, 'billing_email', true );
196
+ $user_email = $user_email ? $user_email : $user->user_email;
197
+ } else {
198
+ $user_email = '';
199
+ }
200
+
201
+ if ( is_add_payment_method_page() ) {
202
+ $pay_button_text = __( 'Add Card', 'woocommerce-gateway-stripe' );
203
+ } else {
204
+ $pay_button_text = '';
205
+ }
206
+
207
+ echo '<div
208
+ id="stripe-payment-data"
209
+ data-panel-label="' . esc_attr( $pay_button_text ) . '"
210
+ data-description=""
211
+ data-email="' . esc_attr( $user_email ) . '"
212
+ data-amount="' . esc_attr( $this->get_stripe_amount( WC()->cart->total ) ) . '"
213
+ data-name="' . esc_attr( sprintf( __( '%s', 'woocommerce-gateway-stripe' ), get_bloginfo( 'name', 'display' ) ) ) . '"
214
+ data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
215
+ data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
216
+ data-bitcoin="' . esc_attr( $this->bitcoin ? 'true' : 'false' ) . '"
217
+ data-locale="' . esc_attr( $this->stripe_checkout_locale ? $this->stripe_checkout_locale : 'en' ) . '">';
218
+
219
+ if ( $this->description ) {
220
+ echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $this->description ) ) );
221
+ }
222
+
223
+ if ( $display_tokenization ) {
224
+ $this->tokenization_script();
225
+ $this->saved_payment_methods();
226
+ }
227
+
228
+ if ( ! $this->stripe_checkout ) {
229
+ $this->form();
230
+
231
+ if ( $display_tokenization ) {
232
+ $this->save_payment_method_checkbox();
233
+ }
234
+ }
235
+
236
+ echo '</div>';
237
+ }
238
+
239
+ /**
240
+ * payment_scripts function.
241
+ *
242
+ * Outputs scripts used for stripe payment
243
+ *
244
+ * @access public
245
+ */
246
+ public function payment_scripts() {
247
+ if ( $this->stripe_checkout ) {
248
+ wp_enqueue_script( 'stripe', 'https://checkout.stripe.com/v2/checkout.js', '', '2.0', true );
249
+ wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe_checkout.js', WC_STRIPE_MAIN_FILE ), array( 'stripe' ), WC_STRIPE_VERSION, true );
250
+ } else {
251
+ wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
252
+ wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe.js', WC_STRIPE_MAIN_FILE ), array( 'jquery-payment', 'stripe' ), WC_STRIPE_VERSION, true );
253
+ }
254
+
255
+ $stripe_params = array(
256
+ 'key' => $this->publishable_key,
257
+ 'i18n_terms' => __( 'Please accept the terms and conditions first', 'woocommerce-gateway-stripe' ),
258
+ 'i18n_required_fields' => __( 'Please fill in required checkout fields first', 'woocommerce-gateway-stripe' ),
259
+ );
260
+
261
+ // If we're on the pay page we need to pass stripe.js the address of the order.
262
+ if ( is_checkout_pay_page() && isset( $_GET['order'] ) && isset( $_GET['order_id'] ) ) {
263
+ $order_key = urldecode( $_GET['order'] );
264
+ $order_id = absint( $_GET['order_id'] );
265
+ $order = wc_get_order( $order_id );
266
+
267
+ if ( $order->id === $order_id && $order->order_key === $order_key ) {
268
+ $stripe_params['billing_first_name'] = $order->billing_first_name;
269
+ $stripe_params['billing_last_name'] = $order->billing_last_name;
270
+ $stripe_params['billing_address_1'] = $order->billing_address_1;
271
+ $stripe_params['billing_address_2'] = $order->billing_address_2;
272
+ $stripe_params['billing_state'] = $order->billing_state;
273
+ $stripe_params['billing_city'] = $order->billing_city;
274
+ $stripe_params['billing_postcode'] = $order->billing_postcode;
275
+ $stripe_params['billing_country'] = $order->billing_country;
276
+ }
277
+ }
278
+
279
+ wp_localize_script( 'woocommerce_stripe', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
280
+ }
281
+
282
+ /**
283
+ * Generate the request for the payment.
284
+ * @param WC_Order $order
285
+ * @param object $source
286
+ * @return array()
287
+ */
288
+ protected function generate_payment_request( $order, $source ) {
289
+ $post_data = array();
290
+ $post_data['currency'] = strtolower( $order->get_order_currency() ? $order->get_order_currency() : get_woocommerce_currency() );
291
+ $post_data['amount'] = $this->get_stripe_amount( $order->get_total(), $post_data['currency'] );
292
+ $post_data['description'] = sprintf( __( '%s - Order %s', 'woocommerce-gateway-stripe' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), $order->get_order_number() );
293
+ $post_data['capture'] = $this->capture ? 'true' : 'false';
294
+
295
+ if ( ! empty( $order->billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) {
296
+ $post_data['receipt_email'] = $order->billing_email;
297
+ }
298
+
299
+ $post_data['expand[]'] = 'balance_transaction';
300
+
301
+ if ( $source->customer ) {
302
+ $post_data['customer'] = $source->customer;
303
+ }
304
+
305
+ if ( $source->source ) {
306
+ $post_data['source'] = $source->source;
307
+ }
308
+
309
+ return $post_data;
310
+ }
311
+
312
+ /**
313
+ * Get payment source. This can be a new token or existing card.
314
+ * @param bool $force_customer Should we force customer creation?
315
+ * @return object
316
+ */
317
+ protected function get_source( $user_id, $force_customer = false ) {
318
+ $stripe_customer = new WC_Stripe_Customer( $user_id );
319
+ $stripe_source = false;
320
+ $token_id = false;
321
+
322
+ // New CC info was entered and we have a new token to process
323
+ if ( isset( $_POST['stripe_token'] ) ) {
324
+ $stripe_token = wc_clean( $_POST['stripe_token'] );
325
+ $maybe_saved_card = ! isset( $_POST['wc-stripe-new-payment-method'] ) || ! empty( $_POST['wc-stripe-new-payment-method'] );
326
+
327
+ // This is true if the user wants to store the card to their account.
328
+ if ( ( $user_id && $this->saved_cards && $maybe_saved_card ) || $force_customer ) {
329
+ $stripe_source = $stripe_customer->add_card( $stripe_token );
330
+
331
+ if ( is_wp_error( $stripe_source ) ) {
332
+ throw new Exception( $stripe_source->get_error_message() );
333
+ }
334
+
335
+ } else {
336
+ // Not saving token, so don't define customer either.
337
+ $stripe_source = $stripe_token;
338
+ $stripe_customer = false;
339
+ }
340
+ }
341
+
342
+ // Use an existing token, and then process the payment
343
+ elseif ( isset( $_POST['wc-stripe-payment-token'] ) && 'new' !== $_POST['wc-stripe-payment-token'] ) {
344
+ $token_id = wc_clean( $_POST['wc-stripe-payment-token'] );
345
+ $token = WC_Payment_Tokens::get( $token_id );
346
+
347
+ if ( ! $token || $token->get_user_id() !== get_current_user_id() ) {
348
+ WC()->session->set( 'refresh_totals', true );
349
+ throw new Exception( __( 'Invalid payment method. Please input a new card number.', 'woocommerce-gateway-stripe' ) );
350
+ }
351
+
352
+ $stripe_source = $token->get_token();
353
+ }
354
+
355
+ return (object) array(
356
+ 'token_id' => $token_id,
357
+ 'customer' => $stripe_customer ? $stripe_customer->get_id() : false,
358
+ 'source' => $stripe_source,
359
+ );
360
+ }
361
+
362
+ /**
363
+ * Get payment source from an order. This could be used in the future for
364
+ * a subscription as an example, therefore using the current user ID would
365
+ * not work - the customer won't be logged in :)
366
+ *
367
+ * Not using 2.6 tokens for this part since we need a customer AND a card
368
+ * token, and not just one.
369
+ *
370
+ * @param object $order
371
+ * @return object
372
+ */
373
+ protected function get_order_source( $order = null ) {
374
+ $stripe_customer = new WC_Stripe_Customer();
375
+ $stripe_source = false;
376
+ $token_id = false;
377
+
378
+ if ( $order ) {
379
+ if ( $meta_value = get_post_meta( $order->id, '_stripe_customer_id', true ) ) {
380
+ $stripe_customer->set_id( $meta_value );
381
+ }
382
+ if ( $meta_value = get_post_meta( $order->id, '_stripe_card_id', true ) ) {
383
+ $stripe_source = $meta_value;
384
+ }
385
+ }
386
+
387
+ return (object) array(
388
+ 'token_id' => $token_id,
389
+ 'customer' => $stripe_customer ? $stripe_customer->get_id() : false,
390
+ 'source' => $stripe_source,
391
+ );
392
+ }
393
+
394
+ /**
395
+ * Process the payment
396
+ */
397
+ public function process_payment( $order_id, $retry = true, $force_customer = false ) {
398
+ try {
399
+ $order = wc_get_order( $order_id );
400
+ $source = $this->get_source( get_current_user_id(), $force_customer );
401
+
402
+ if ( empty( $source->source ) && empty( $source->customer ) ) {
403
+ $error_msg = __( 'Please enter your card details to make a payment.', 'woocommerce-gateway-stripe' );
404
+ $error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce-gateway-stripe' );
405
+ throw new Exception( $error_msg );
406
+ }
407
+
408
+ // Store source to order meta
409
+ $this->save_source( $order, $source );
410
+
411
+ // Handle payment
412
+ if ( $order->get_total() > 0 ) {
413
+
414
+ if ( $order->get_total() * 100 < 50 ) {
415
+ throw new Exception( __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce-gateway-stripe' ) );
416
+ }
417
+
418
+ WC_Stripe::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
419
+
420
+ // Make the request
421
+ $response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
422
+
423
+ if ( is_wp_error( $response ) ) {
424
+ // Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
425
+ if ( 'customer' === $response->get_error_code() && $retry ) {
426
+ delete_user_meta( get_current_user_id(), '_stripe_customer_id' );
427
+ return $this->process_payment( $order_id, false, $force_customer );
428
+ // Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
429
+ } elseif ( 'source' === $response->get_error_code() && $source->token_id ) {
430
+ $token = WC_Payment_Tokens::get( $source->token_id );
431
+ $token->delete();
432
+ throw new Exception( __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' ) );
433
+ }
434
+ throw new Exception( $response->get_error_code() . ': ' . $response->get_error_message() );
435
+ }
436
+
437
+ // Process valid response
438
+ $this->process_response( $response, $order );
439
+ } else {
440
+ $order->payment_complete();
441
+ }
442
+
443
+ // Remove cart
444
+ WC()->cart->empty_cart();
445
+
446
+ // Return thank you page redirect
447
+ return array(
448
+ 'result' => 'success',
449
+ 'redirect' => $this->get_return_url( $order )
450
+ );
451
+
452
+ } catch ( Exception $e ) {
453
+ wc_add_notice( $e->getMessage(), 'error' );
454
+ WC()->session->set( 'refresh_totals', true );
455
+ WC_Stripe::log( sprintf( __( 'Error: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
456
+ return;
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Save source to order.
462
+ */
463
+ protected function save_source( $order, $source ) {
464
+ // Store source in the order
465
+ if ( $source->customer ) {
466
+ update_post_meta( $order->id, '_stripe_customer_id', $source->customer );
467
+ }
468
+ if ( $source->source ) {
469
+ update_post_meta( $order->id, '_stripe_card_id', $source->source );
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Store extra meta data for an order from a Stripe Response.
475
+ */
476
+ public function process_response( $response, $order ) {
477
+ WC_Stripe::log( "Processing response: " . print_r( $response, true ) );
478
+
479
+ // Store charge data
480
+ update_post_meta( $order->id, '_stripe_charge_id', $response->id );
481
+ update_post_meta( $order->id, '_stripe_charge_captured', $response->captured ? 'yes' : 'no' );
482
+
483
+ // Store other data such as fees
484
+ if ( isset( $response->balance_transaction ) && isset( $response->balance_transaction->fee ) ) {
485
+ $fee = number_format( $response->balance_transaction->fee / 100, 2, '.', '' );
486
+ update_post_meta( $order->id, 'Stripe Fee', $fee );
487
+ update_post_meta( $order->id, 'Net Revenue From Stripe', $order->get_total() - $fee );
488
+ }
489
+
490
+ if ( $response->captured ) {
491
+ $order->payment_complete( $response->id );
492
+ WC_Stripe::log( "Successful charge: $response->id" );
493
+ } else {
494
+ add_post_meta( $order->id, '_transaction_id', $response->id, true );
495
+
496
+ if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
497
+ $order->reduce_order_stock();
498
+ }
499
+
500
+ $order->update_status( 'on-hold', sprintf( __( 'Stripe charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization.', 'woocommerce-gateway-stripe' ), $response->id ) );
501
+ WC_Stripe::log( "Successful auth: $response->id" );
502
+ }
503
+
504
+ return $response;
505
+ }
506
+
507
+ /**
508
+ * Add payment method via account screen.
509
+ * We don't store the token locally, but to the Stripe API.
510
+ * @since 3.0.0
511
+ */
512
+ public function add_payment_method() {
513
+ if ( empty( $_POST['stripe_token'] ) || ! is_user_logged_in() ) {
514
+ wc_add_notice( __( 'There was a problem adding the card.', 'woocommerce-gateway-stripe' ), 'error' );
515
+ return;
516
+ }
517
+
518
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
519
+ $card = $stripe_customer->add_card( wc_clean( $_POST['stripe_token'] ) );
520
+
521
+ if ( is_wp_error( $card ) ) {
522
+ throw new Exception( $card->get_error_message() );
523
+ }
524
+
525
+ return array(
526
+ 'result' => 'success',
527
+ 'redirect' => wc_get_endpoint_url( 'payment-methods' ),
528
+ );
529
+ }
530
+
531
+ /**
532
+ * Refund a charge
533
+ * @param int $order_id
534
+ * @param float $amount
535
+ * @return bool
536
+ */
537
+ public function process_refund( $order_id, $amount = null, $reason = '' ) {
538
+ $order = wc_get_order( $order_id );
539
+
540
+ if ( ! $order || ! $order->get_transaction_id() ) {
541
+ return false;
542
+ }
543
+
544
+ $body = array();
545
+
546
+ if ( ! is_null( $amount ) ) {
547
+ $body['amount'] = $this->get_stripe_amount( $amount );
548
+ }
549
+
550
+ if ( $reason ) {
551
+ $body['metadata'] = array(
552
+ 'reason' => $reason,
553
+ );
554
+ }
555
+
556
+ WC_Stripe::log( "Info: Beginning refund for order $order_id for the amount of {$amount}" );
557
+
558
+ $response = WC_Stripe_API::request( $body, 'charges/' . $order->get_transaction_id() . '/refunds' );
559
+
560
+ if ( is_wp_error( $response ) ) {
561
+ WC_Stripe::log( "Error: " . $response->get_error_message() );
562
+ return $response;
563
+ } elseif ( ! empty( $response->id ) ) {
564
+ $refund_message = sprintf( __( 'Refunded %s - Refund ID: %s - Reason: %s', 'woocommerce-gateway-stripe' ), wc_price( $response->amount / 100 ), $response->id, $reason );
565
+ $order->add_order_note( $refund_message );
566
+ WC_Stripe::log( "Success: " . html_entity_decode( strip_tags( $refund_message ) ) );
567
+ return true;
568
+ }
569
+ }
570
+ }
includes/class-wc-stripe-api.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ /**
7
+ * WC_Stripe_API class.
8
+ *
9
+ * Communicates with Stripe API.
10
+ */
11
+ class WC_Stripe_API {
12
+
13
+ /**
14
+ * Stripe API Endpoint
15
+ */
16
+ const ENDPOINT = 'https://api.stripe.com/v1/';
17
+
18
+ /**
19
+ * Secret API Key.
20
+ * @var string
21
+ */
22
+ private static $secret_key = '';
23
+
24
+ /**
25
+ * Set secret API Key.
26
+ * @param string $key
27
+ */
28
+ public static function set_secret_key( $secret_key ) {
29
+ self::$secret_key = $secret_key;
30
+ }
31
+
32
+ /**
33
+ * Get secret key.
34
+ * @return string
35
+ */
36
+ public static function get_secret_key() {
37
+ if ( ! self::$secret_key ) {
38
+ $options = get_option( 'woocommerce_stripe_settings' );
39
+
40
+ if ( isset( $options['testmode'], $options['secret_key'], $options['test_secret_key'] ) ) {
41
+ self::set_secret_key( 'yes' === $options['testmode'] ? $options['test_secret_key'] : $options['secret_key'] );
42
+ }
43
+ }
44
+ return self::$secret_key;
45
+ }
46
+
47
+ /**
48
+ * Send the request to Stripe's API
49
+ *
50
+ * @param array $request
51
+ * @param string $api
52
+ * @return array|WP_Error
53
+ */
54
+ public static function request( $request, $api = 'charges', $method = 'POST' ) {
55
+ WC_Stripe::log( "{$api} request: " . print_r( $request, true ) );
56
+
57
+ $response = wp_safe_remote_post(
58
+ self::ENDPOINT . $api,
59
+ array(
60
+ 'method' => $method,
61
+ 'headers' => array(
62
+ 'Authorization' => 'Basic ' . base64_encode( self::get_secret_key(). ':' ),
63
+ 'Stripe-Version' => '2016-03-07'
64
+ ),
65
+ 'body' => apply_filters( 'woocommerce_stripe_request_body', $request, $api ),
66
+ 'timeout' => 70,
67
+ 'user-agent' => 'WooCommerce ' . WC()->version
68
+ )
69
+ );
70
+
71
+ if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
72
+ WC_Stripe::log( "Error Response: " . print_r( $response, true ) );
73
+ return new WP_Error( 'stripe_error', __( 'There was a problem connecting to the payment gateway.', 'woocommerce-gateway-stripe' ) );
74
+ }
75
+
76
+ $parsed_response = json_decode( $response['body'] );
77
+ // Handle response
78
+ if ( ! empty( $parsed_response->error ) ) {
79
+ if ( ! empty( $parsed_response->error->param ) ) {
80
+ $code = $parsed_response->error->param;
81
+ } elseif ( ! empty( $parsed_response->error->code ) ) {
82
+ $code = $parsed_response->error->code;
83
+ } else {
84
+ $code = 'stripe_error';
85
+ }
86
+ return new WP_Error( $code, $parsed_response->error->message );
87
+ } else {
88
+ return $parsed_response;
89
+ }
90
+ }
91
+ }
includes/class-wc-stripe-customer.php ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ /**
7
+ * WC_Stripe_Customer class.
8
+ *
9
+ * Represents a Stripe Customer.
10
+ */
11
+ class WC_Stripe_Customer {
12
+
13
+ /**
14
+ * Stripe customer ID
15
+ * @var string
16
+ */
17
+ private $id = '';
18
+
19
+ /**
20
+ * WP User ID
21
+ * @var integer
22
+ */
23
+ private $user_id = 0;
24
+
25
+ /**
26
+ * Data from API
27
+ * @var array
28
+ */
29
+ private $customer_data = array();
30
+
31
+ /**
32
+ * Constructor
33
+ * @param integer $user_id
34
+ */
35
+ public function __construct( $user_id = 0 ) {
36
+ if ( $user_id ) {
37
+ $this->set_user_id( $user_id );
38
+ $this->set_id( get_user_meta( $user_id, '_stripe_customer_id', true ) );
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Get Stripe customer ID.
44
+ * @return string
45
+ */
46
+ public function get_id() {
47
+ return $this->id;
48
+ }
49
+
50
+ /**
51
+ * Set Stripe customer ID.
52
+ * @param [type] $id [description]
53
+ */
54
+ public function set_id( $id ) {
55
+ $this->id = wc_clean( $id );
56
+ }
57
+
58
+ /**
59
+ * User ID in WordPress.
60
+ * @return int
61
+ */
62
+ public function get_user_id() {
63
+ return absint( $this->user_id );
64
+ }
65
+
66
+ /**
67
+ * Set User ID used by WordPress.
68
+ * @param int $user_id
69
+ */
70
+ public function set_user_id( $user_id ) {
71
+ $this->user_id = absint( $user_id );
72
+ }
73
+
74
+ /**
75
+ * Get user object.
76
+ * @return WP_User
77
+ */
78
+ protected function get_user() {
79
+ return $this->get_user_id() ? get_user_by( 'id', $this->get_user_id() ) : false;
80
+ }
81
+
82
+ /**
83
+ * Store data from the Stripe API about this customer
84
+ */
85
+ public function set_customer_data( $data ) {
86
+ $this->customer_data = $data;
87
+ }
88
+
89
+ /**
90
+ * Get data from the Stripe API about this customer
91
+ */
92
+ public function get_customer_data() {
93
+ if ( empty( $this->customer_data ) && $this->get_id() && false === ( $this->customer_data = get_transient( 'stripe_customer_' . $this->get_id() ) ) ) {
94
+ $response = WC_Stripe_API::request( array(), 'customers/' . $this->get_id() );
95
+
96
+ if ( ! is_wp_error( $response ) ) {
97
+ $this->set_customer_data( $response );
98
+ set_transient( 'stripe_customer_' . $this->get_id(), $response, HOUR_IN_SECONDS * 48 );
99
+ }
100
+ }
101
+ return $this->customer_data;
102
+ }
103
+
104
+ /**
105
+ * Get default card/source
106
+ * @return string
107
+ */
108
+ public function get_default_card() {
109
+ $data = $this->get_customer_data();
110
+ $source = '';
111
+
112
+ if ( $data ) {
113
+ $source = $data->default_source;
114
+ }
115
+
116
+ return $source;
117
+ }
118
+
119
+ /**
120
+ * Create a customer via API.
121
+ * @param array $args
122
+ * @return WP_Error|int
123
+ */
124
+ public function create_customer( $args ) {
125
+ if ( $user = $this->get_user() ) {
126
+ $defaults = array(
127
+ 'email' => $user->user_email,
128
+ 'description' => $user->display_name,
129
+ );
130
+ } else {
131
+ $defaults = array(
132
+ 'email' => '',
133
+ 'description' => '',
134
+ );
135
+ }
136
+
137
+ $args = wp_parse_args( $args, $defaults );
138
+ $response = WC_Stripe_API::request( $args, 'customers' );
139
+
140
+ if ( is_wp_error( $response ) ) {
141
+ return $response;
142
+ } elseif ( empty( $response->id ) ) {
143
+ return new WP_Error( 'stripe_error', __( 'Could not create Stripe customer.', 'woocommerce-gateway-stripe' ) );
144
+ }
145
+
146
+ $this->set_id( $response->id );
147
+ $this->clear_cache();
148
+ $this->set_customer_data( $response );
149
+
150
+ if ( $this->get_user_id() ) {
151
+ update_user_meta( $this->get_user_id(), '_stripe_customer_id', $response->id );
152
+ }
153
+
154
+ do_action( 'woocommerce_stripe_add_customer', $args, $response );
155
+
156
+ return $response->id;
157
+ }
158
+
159
+ /**
160
+ * Add a card for this stripe customer.
161
+ * @param string $token
162
+ * @param bool $retry
163
+ * @return WP_Error|int
164
+ */
165
+ public function add_card( $token, $retry = true ) {
166
+ if ( ! $this->get_id() ) {
167
+ if ( ( $response = $this->create_customer() ) && is_wp_error( $response ) ) {
168
+ return $response;
169
+ }
170
+ }
171
+
172
+ $response = WC_Stripe_API::request( array(
173
+ 'source' => $token
174
+ ), 'customers/' . $this->get_id() . '/sources' );
175
+
176
+ if ( is_wp_error( $response ) ) {
177
+ if ( 'customer' === $response->get_error_code() && $retry ) {
178
+ $this->create_customer();
179
+ return $this->add_card( $token, false );
180
+ } else {
181
+ return $response;
182
+ }
183
+ } elseif ( empty( $response->id ) ) {
184
+ return new WP_Error( 'error', __( 'Unable to add card', 'woocommerce-gateway-stripe' ) );
185
+ }
186
+
187
+ // Add token to WooCommerce
188
+ if ( $this->get_user_id() ) {
189
+ $token = new WC_Payment_Token_CC();
190
+ $token->set_token( $response->id );
191
+ $token->set_gateway_id( 'stripe' );
192
+ $token->set_card_type( strtolower( $response->brand ) );
193
+ $token->set_last4( $response->last4 );
194
+ $token->set_expiry_month( $response->exp_month );
195
+ $token->set_expiry_year( $response->exp_year );
196
+ $token->set_user_id( $this->get_user_id() );
197
+ $token->save();
198
+ }
199
+
200
+ $this->clear_cache();
201
+
202
+ do_action( 'woocommerce_stripe_add_card', $this->get_id(), $token, $response );
203
+
204
+ return $response->id;
205
+ }
206
+
207
+ /**
208
+ * Get a customers saved cards using their Stripe ID. Cached.
209
+ *
210
+ * @param string $customer_id
211
+ * @return array
212
+ */
213
+ public function get_cards() {
214
+ $cards = array();
215
+
216
+ if ( $this->get_id() && false === ( $cards = get_transient( 'stripe_cards_' . $this->get_id() ) ) ) {
217
+ $response = WC_Stripe_API::request( array(
218
+ 'limit' => 100
219
+ ), 'customers/' . $this->get_id() . '/sources', 'GET' );
220
+
221
+ if ( is_wp_error( $response ) ) {
222
+ return array();
223
+ }
224
+
225
+ if ( is_array( $response->data ) ) {
226
+ $cards = $response->data;
227
+ }
228
+
229
+ set_transient( 'stripe_cards_' . $this->get_id(), $cards, HOUR_IN_SECONDS * 48 );
230
+ }
231
+
232
+ return $cards;
233
+ }
234
+
235
+ /**
236
+ * Delete a card from stripe.
237
+ * @param string $card_id
238
+ */
239
+ public function delete_card( $card_id ) {
240
+ $response = WC_Stripe_API::request( array(), 'customers/' . $this->get_id() . '/sources/' . sanitize_text_field( $card_id ), 'DELETE' );
241
+
242
+ $this->clear_cache();
243
+
244
+ if ( ! is_wp_error( $response ) ) {
245
+ do_action( 'wc_stripe_delete_card', $this->get_id(), $response );
246
+
247
+ return true;
248
+ }
249
+
250
+ return false;
251
+ }
252
+
253
+ /**
254
+ * Set default card in Stripe
255
+ * @param string $card_id
256
+ */
257
+ public function set_default_card( $card_id ) {
258
+ $response = WC_Stripe_API::request( array(
259
+ 'default_source' => sanitize_text_field( $card_id ),
260
+ ), 'customers/' . $this->get_id(), 'POST' );
261
+
262
+ $this->clear_cache();
263
+
264
+ if ( ! is_wp_error( $response ) ) {
265
+ do_action( 'wc_stripe_set_default_card', $this->get_id(), $response );
266
+
267
+ return true;
268
+ }
269
+
270
+ return false;
271
+ }
272
+
273
+ /**
274
+ * Deletes caches for this users cards.
275
+ */
276
+ public function clear_cache() {
277
+ delete_transient( 'stripe_cards_' . $this->get_id() );
278
+ delete_transient( 'stripe_customer_' . $this->get_id() );
279
+ $this->customer_data = array();
280
+ }
281
+ }
includes/legacy/class-wc-gateway-stripe-saved-cards.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ /**
7
+ * WC_Gateway_Stripe_Saved_Cards class.
8
+ */
9
+ class WC_Gateway_Stripe_Saved_Cards {
10
+
11
+ /**
12
+ * Constructor
13
+ */
14
+ public function __construct() {
15
+ add_action( 'wp', array( $this, 'delete_card' ) );
16
+ add_action( 'woocommerce_after_my_account', array( $this, 'output' ) );
17
+ add_action( 'wp', array( $this, 'default_card' ) );
18
+ }
19
+
20
+ /**
21
+ * Display saved cards
22
+ */
23
+ public function output() {
24
+ if ( ! is_user_logged_in() ) {
25
+ return;
26
+ }
27
+
28
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
29
+ $stripe_cards = $stripe_customer->get_cards();
30
+ $default_card = $stripe_customer->get_default_card();
31
+
32
+ if ( $stripe_cards ) {
33
+ wc_get_template( 'saved-cards.php', array( 'cards' => $stripe_cards, 'default_card' => $default_card ), 'woocommerce-gateway-stripe/', untrailingslashit( plugin_dir_path( WC_STRIPE_MAIN_FILE ) ) . '/includes/legacy/templates/' );
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Delete a card
39
+ */
40
+ public function delete_card() {
41
+ if ( ! isset( $_POST['stripe_delete_card'] ) || ! is_account_page() ) {
42
+ return;
43
+ }
44
+
45
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
46
+ $stripe_customer_id = $stripe_customer->get_id();
47
+ $delete_card = sanitize_text_field( $_POST['stripe_delete_card'] );
48
+
49
+ if ( ! is_user_logged_in() || ! $stripe_customer_id || ! wp_verify_nonce( $_POST['_wpnonce'], "stripe_del_card" ) ) {
50
+ wp_die( __( 'Unable to make default card, please try again', 'woocommerce-gateway-stripe' ) );
51
+ }
52
+
53
+ if ( ! $stripe_customer->delete_card( $delete_card ) ) {
54
+ wc_add_notice( __( 'Unable to delete card.', 'woocommerce-gateway-stripe' ), 'error' );
55
+ } else {
56
+ wc_add_notice( __( 'Card deleted.', 'woocommerce-gateway-stripe' ), 'success' );
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Make a card as default method
62
+ */
63
+ public function default_card() {
64
+ if ( ! isset( $_POST['stripe_default_card'] ) || ! is_account_page() ) {
65
+ return;
66
+ }
67
+
68
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
69
+ $stripe_customer_id = $stripe_customer->get_id();
70
+ $default_source = sanitize_text_field( $_POST['stripe_default_card'] );
71
+
72
+ if ( ! is_user_logged_in() || ! $stripe_customer_id || ! wp_verify_nonce( $_POST['_wpnonce'], "stripe_default_card" ) ) {
73
+ wp_die( __( 'Unable to make default card, please try again', 'woocommerce-gateway-stripe' ) );
74
+ }
75
+
76
+ if ( ! $stripe_customer->set_default_card( $default_source ) ) {
77
+ wc_add_notice( __( 'Unable to update default card.', 'woocommerce-gateway-stripe' ), 'error' );
78
+ } else {
79
+ wc_add_notice( __( 'Default card updated.', 'woocommerce-gateway-stripe' ), 'success' );
80
+ }
81
+ }
82
+ }
83
+ new WC_Gateway_Stripe_Saved_Cards();
includes/legacy/class-wc-gateway-stripe.php ADDED
@@ -0,0 +1,596 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ /**
7
+ * WC_Gateway_Stripe class.
8
+ *
9
+ * @extends WC_Payment_Gateway
10
+ */
11
+ class WC_Gateway_Stripe extends WC_Payment_Gateway {
12
+
13
+ /**
14
+ * Constructor
15
+ */
16
+ public function __construct() {
17
+ $this->id = 'stripe';
18
+ $this->method_title = __( 'Stripe', 'woocommerce-gateway-stripe' );
19
+ $this->method_description = __( 'Stripe works by adding credit card fields on the checkout and then sending the details to Stripe for verification.', 'woocommerce-gateway-stripe' );
20
+ $this->has_fields = true;
21
+ $this->view_transaction_url = 'https://dashboard.stripe.com/payments/%s';
22
+ $this->supports = array(
23
+ 'subscriptions',
24
+ 'products',
25
+ 'refunds',
26
+ 'subscription_cancellation',
27
+ 'subscription_reactivation',
28
+ 'subscription_suspension',
29
+ 'subscription_amount_changes',
30
+ 'subscription_payment_method_change', // Subs 1.n compatibility
31
+ 'subscription_payment_method_change_customer',
32
+ 'subscription_payment_method_change_admin',
33
+ 'subscription_date_changes',
34
+ 'multiple_subscriptions',
35
+ 'pre-orders',
36
+ );
37
+
38
+ // Load the form fields
39
+ $this->init_form_fields();
40
+
41
+ // Load the settings.
42
+ $this->init_settings();
43
+
44
+ // Get setting values.
45
+ $this->title = $this->get_option( 'title' );
46
+ $this->description = $this->get_option( 'description' );
47
+ $this->enabled = $this->get_option( 'enabled' );
48
+ $this->testmode = 'yes' === $this->get_option( 'testmode' );
49
+ $this->capture = 'yes' === $this->get_option( 'capture', 'yes' );
50
+ $this->stripe_checkout = 'yes' === $this->get_option( 'stripe_checkout' );
51
+ $this->stripe_checkout_locale = $this->get_option( 'stripe_checkout_locale' );
52
+ $this->stripe_checkout_image = $this->get_option( 'stripe_checkout_image', '' );
53
+ $this->saved_cards = 'yes' === $this->get_option( 'saved_cards' );
54
+ $this->secret_key = $this->testmode ? $this->get_option( 'test_secret_key' ) : $this->get_option( 'secret_key' );
55
+ $this->publishable_key = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
56
+ $this->bitcoin = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' );
57
+ $this->logging = 'yes' === $this->get_option( 'logging' );
58
+
59
+ if ( $this->stripe_checkout ) {
60
+ $this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' );
61
+ }
62
+
63
+ if ( $this->testmode ) {
64
+ $this->description .= ' ' . sprintf( __( 'TEST MODE ENABLED. In test mode, you can use the card number 4242424242424242 with any CVC and a valid expiration date or check the documentation "<a href="%s">Testing Stripe</a>" for more card numbers.', 'woocommerce-gateway-stripe' ), 'https://stripe.com/docs/testing' );
65
+ $this->description = trim( $this->description );
66
+ }
67
+
68
+ WC_Stripe_API::set_secret_key( $this->secret_key );
69
+
70
+ // Hooks
71
+ add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
72
+ add_action( 'admin_notices', array( $this, 'admin_notices' ) );
73
+ add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
74
+ }
75
+
76
+ /**
77
+ * get_icon function.
78
+ *
79
+ * @access public
80
+ * @return string
81
+ */
82
+ public function get_icon() {
83
+ $ext = version_compare( WC()->version, '2.6', '>=' ) ? '.svg' : '.png';
84
+ $style = version_compare( WC()->version, '2.6', '>=' ) ? 'style="margin-left: 0.3em"' : '';
85
+
86
+ $icon = '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/visa' . $ext ) . '" alt="Visa" width="32" ' . $style . ' />';
87
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/mastercard' . $ext ) . '" alt="Mastercard" width="32" ' . $style . ' />';
88
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/amex' . $ext ) . '" alt="Amex" width="32" ' . $style . ' />';
89
+
90
+ if ( 'USD' === get_woocommerce_currency() ) {
91
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/discover' . $ext ) . '" alt="Discover" width="32" ' . $style . ' />';
92
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/jcb' . $ext ) . '" alt="JCB" width="32" ' . $style . ' />';
93
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/diners' . $ext ) . '" alt="Diners" width="32" ' . $style . ' />';
94
+ }
95
+
96
+ if ( $this->bitcoin ) {
97
+ $icon .= '<img src="' . WC_HTTPS::force_https_url( plugins_url( '/assets/images/bitcoin' . $ext, WC_STRIPE_MAIN_FILE ) ) . '" alt="Bitcoin" width="32" ' . $style . ' />';
98
+ }
99
+
100
+ return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id );
101
+ }
102
+
103
+ /**
104
+ * Get Stripe amount to pay
105
+ * @return float
106
+ */
107
+ public function get_stripe_amount( $total, $currency = '' ) {
108
+ if ( ! $currency ) {
109
+ $currency = get_woocommerce_currency();
110
+ }
111
+ switch ( strtoupper( $currency ) ) {
112
+ // Zero decimal currencies
113
+ case 'BIF' :
114
+ case 'CLP' :
115
+ case 'DJF' :
116
+ case 'GNF' :
117
+ case 'JPY' :
118
+ case 'KMF' :
119
+ case 'KRW' :
120
+ case 'MGA' :
121
+ case 'PYG' :
122
+ case 'RWF' :
123
+ case 'VND' :
124
+ case 'VUV' :
125
+ case 'XAF' :
126
+ case 'XOF' :
127
+ case 'XPF' :
128
+ $total = absint( $total );
129
+ break;
130
+ default :
131
+ $total = round( $total, 2 ) * 100; // In cents
132
+ break;
133
+ }
134
+ return $total;
135
+ }
136
+
137
+ /**
138
+ * Check if SSL is enabled and notify the user
139
+ */
140
+ public function admin_notices() {
141
+ if ( $this->enabled == 'no' ) {
142
+ return;
143
+ }
144
+
145
+ $addons = ( class_exists( 'WC_Subscriptions_Order' ) || class_exists( 'WC_Pre_Orders_Order' ) ) ? '_addons' : '';
146
+
147
+ // Check required fields
148
+ if ( ! $this->secret_key ) {
149
+ echo '<div class="error"><p>' . sprintf( __( 'Stripe error: Please enter your secret key <a href="%s">here</a>', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout&section=wc_gateway_stripe' . $addons ) ) . '</p></div>';
150
+ return;
151
+
152
+ } elseif ( ! $this->publishable_key ) {
153
+ echo '<div class="error"><p>' . sprintf( __( 'Stripe error: Please enter your publishable key <a href="%s">here</a>', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout&section=wc_gateway_stripe' . $addons ) ) . '</p></div>';
154
+ return;
155
+ }
156
+
157
+ // Simple check for duplicate keys
158
+ if ( $this->secret_key == $this->publishable_key ) {
159
+ echo '<div class="error"><p>' . sprintf( __( 'Stripe error: Your secret and publishable keys match. Please check and re-enter.', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout&section=wc_gateway_stripe' . $addons ) ) . '</p></div>';
160
+ return;
161
+ }
162
+
163
+ // Show message if enabled and FORCE SSL is disabled and WordpressHTTPS plugin is not detected
164
+ if ( ( function_exists( 'wc_site_is_https' ) && ! wc_site_is_https() ) && ( 'no' === get_option( 'woocommerce_force_ssl_checkout' ) && ! class_exists( 'WordPressHTTPS' ) ) ) {
165
+ echo '<div class="error"><p>' . sprintf( __( 'Stripe is enabled, but the <a href="%s">force SSL option</a> is disabled; your checkout may not be secure! Please enable SSL and ensure your server has a valid SSL certificate - Stripe will only work in test mode.', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout' ) ) . '</p></div>';
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Check if this gateway is enabled
171
+ */
172
+ public function is_available() {
173
+ if ( 'yes' === $this->enabled ) {
174
+ if ( ! $this->testmode && is_checkout() && ! is_ssl() ) {
175
+ return false;
176
+ }
177
+ if ( ! $this->secret_key || ! $this->publishable_key ) {
178
+ return false;
179
+ }
180
+ return true;
181
+ }
182
+ return false;
183
+ }
184
+
185
+ /**
186
+ * Initialise Gateway Settings Form Fields
187
+ */
188
+ public function init_form_fields() {
189
+ $this->form_fields = include( untrailingslashit( plugin_dir_path( WC_STRIPE_MAIN_FILE ) ) . '/includes/settings-stripe.php' );
190
+
191
+ wc_enqueue_js( "
192
+ jQuery( function( $ ) {
193
+ $( '#woocommerce_stripe_stripe_checkout' ).change(function(){
194
+ if ( $( this ).is( ':checked' ) ) {
195
+ $( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).show();
196
+ } else {
197
+ $( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).hide();
198
+ }
199
+ }).change();
200
+ });
201
+ " );
202
+ }
203
+
204
+ /**
205
+ * Payment form on checkout page
206
+ */
207
+ public function payment_fields() {
208
+ ?>
209
+ <fieldset class="stripe-legacy-payment-fields">
210
+ <?php
211
+ if ( $this->description ) {
212
+ echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $this->description ) ) );
213
+ }
214
+ if ( $this->saved_cards && is_user_logged_in() ) {
215
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
216
+ ?>
217
+ <p class="form-row form-row-wide">
218
+ <a class="<?php echo apply_filters( 'wc_stripe_manage_saved_cards_class', 'button' ); ?>" style="float:right;" href="<?php echo apply_filters( 'wc_stripe_manage_saved_cards_url', get_permalink( get_option( 'woocommerce_myaccount_page_id' ) ) ); ?>#saved-cards"><?php esc_html_e( 'Manage cards', 'woocommerce-gateway-stripe' ); ?></a>
219
+ <?php
220
+ if ( $cards = $stripe_customer->get_cards() ) {
221
+ $default_card = $cards[0]->id;
222
+ foreach ( (array) $cards as $card ) {
223
+ if ( 'card' !== $card->object ) {
224
+ continue;
225
+ }
226
+ ?>
227
+ <label for="stripe_card_<?php echo $card->id; ?>" class="brand-<?php echo esc_attr( strtolower( $card->brand ) ); ?>">
228
+ <input type="radio" id="stripe_card_<?php echo $card->id; ?>" name="wc-stripe-payment-token" value="<?php echo $card->id; ?>" <?php checked( $default_card, $card->id ) ?> />
229
+ <?php printf( __( '%s card ending in %s (Expires %s/%s)', 'woocommerce-gateway-stripe' ), $card->brand, $card->last4, $card->exp_month, $card->exp_year ); ?>
230
+ </label>
231
+ <?php
232
+ }
233
+ }
234
+ ?>
235
+ <label for="new">
236
+ <input type="radio" id="new" name="wc-stripe-payment-token" value="new" />
237
+ <?php _e( 'Use a new credit card', 'woocommerce-gateway-stripe' ); ?>
238
+ </label>
239
+ </p>
240
+ <?php
241
+ }
242
+
243
+ $user = wp_get_current_user();
244
+
245
+ if ( $user ) {
246
+ $user_email = get_user_meta( $user->ID, 'billing_email', true );
247
+ $user_email = $user_email ? $user_email : $user->user_email;
248
+ } else {
249
+ $user_email = '';
250
+ }
251
+
252
+ $display = '';
253
+
254
+ if ( $this->stripe_checkout || $this->saved_cards && ! empty( $cards ) ) {
255
+ $display = 'style="display:none;"';
256
+ }
257
+
258
+ echo '<div ' . $display . ' id="stripe-payment-data"
259
+ data-description=""
260
+ data-email="' . esc_attr( $user_email ) . '"
261
+ data-amount="' . esc_attr( $this->get_stripe_amount( WC()->cart->total ) ) . '"
262
+ data-name="' . esc_attr( sprintf( __( '%s', 'woocommerce-gateway-stripe' ), get_bloginfo( 'name', 'display' ) ) ) . '"
263
+ data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
264
+ data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
265
+ data-bitcoin="' . esc_attr( $this->bitcoin ? 'true' : 'false' ) . '"
266
+ data-locale="' . esc_attr( $this->stripe_checkout_locale ? $this->stripe_checkout_locale : 'en' ) . '">';
267
+
268
+ if ( ! $this->stripe_checkout ) {
269
+ $this->credit_card_form( array( 'fields_have_names' => false ) );
270
+ }
271
+
272
+ echo '</div>';
273
+ ?>
274
+ </fieldset>
275
+ <?php
276
+ }
277
+
278
+ /**
279
+ * payment_scripts function.
280
+ *
281
+ * Outputs scripts used for stripe payment
282
+ *
283
+ * @access public
284
+ */
285
+ public function payment_scripts() {
286
+ if ( $this->stripe_checkout ) {
287
+ wp_enqueue_script( 'stripe', 'https://checkout.stripe.com/v2/checkout.js', '', '2.0', true );
288
+ wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe_checkout.js', WC_STRIPE_MAIN_FILE ), array( 'stripe' ), WC_STRIPE_VERSION, true );
289
+ } else {
290
+ wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
291
+ wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe.js', WC_STRIPE_MAIN_FILE ), array( 'jquery-payment', 'stripe' ), WC_STRIPE_VERSION, true );
292
+ }
293
+
294
+ $stripe_params = array(
295
+ 'key' => $this->publishable_key,
296
+ 'i18n_terms' => __( 'Please accept the terms and conditions first', 'woocommerce-gateway-stripe' ),
297
+ 'i18n_required_fields' => __( 'Please fill in required checkout fields first', 'woocommerce-gateway-stripe' ),
298
+ );
299
+
300
+ // If we're on the pay page we need to pass stripe.js the address of the order.
301
+ if ( is_checkout_pay_page() && isset( $_GET['order'] ) && isset( $_GET['order_id'] ) ) {
302
+ $order_key = urldecode( $_GET['order'] );
303
+ $order_id = absint( $_GET['order_id'] );
304
+ $order = wc_get_order( $order_id );
305
+
306
+ if ( $order->id === $order_id && $order->order_key === $order_key ) {
307
+ $stripe_params['billing_first_name'] = $order->billing_first_name;
308
+ $stripe_params['billing_last_name'] = $order->billing_last_name;
309
+ $stripe_params['billing_address_1'] = $order->billing_address_1;
310
+ $stripe_params['billing_address_2'] = $order->billing_address_2;
311
+ $stripe_params['billing_state'] = $order->billing_state;
312
+ $stripe_params['billing_city'] = $order->billing_city;
313
+ $stripe_params['billing_postcode'] = $order->billing_postcode;
314
+ $stripe_params['billing_country'] = $order->billing_country;
315
+ }
316
+ }
317
+
318
+ wp_localize_script( 'woocommerce_stripe', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
319
+ }
320
+
321
+ /**
322
+ * Generate the request for the payment.
323
+ * @param WC_Order $order
324
+ * @param object $source
325
+ * @return array()
326
+ */
327
+ protected function generate_payment_request( $order, $source ) {
328
+ $post_data = array();
329
+ $post_data['currency'] = strtolower( $order->get_order_currency() ? $order->get_order_currency() : get_woocommerce_currency() );
330
+ $post_data['amount'] = $this->get_stripe_amount( $order->get_total(), $post_data['currency'] );
331
+ $post_data['description'] = sprintf( __( '%s - Order %s', 'woocommerce-gateway-stripe' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), $order->get_order_number() );
332
+ $post_data['capture'] = $this->capture ? 'true' : 'false';
333
+
334
+ if ( ! empty( $order->billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) {
335
+ $post_data['receipt_email'] = $order->billing_email;
336
+ }
337
+
338
+ $post_data['expand[]'] = 'balance_transaction';
339
+
340
+ if ( $source->customer ) {
341
+ $post_data['customer'] = $source->customer;
342
+ }
343
+
344
+ if ( $source->source ) {
345
+ $post_data['source'] = $source->source;
346
+ }
347
+
348
+ return $post_data;
349
+ }
350
+
351
+ /**
352
+ * Get payment source. This can be a new token or existing card.
353
+ * @param bool $force_customer Should we force customer creation?
354
+ * @return object
355
+ */
356
+ protected function get_source( $user_id, $force_customer = false ) {
357
+ $stripe_customer = new WC_Stripe_Customer( $user_id );
358
+ $stripe_source = false;
359
+ $token_id = false;
360
+
361
+ // New CC info was entered and we have a new token to process
362
+ if ( isset( $_POST['stripe_token'] ) ) {
363
+ $stripe_token = wc_clean( $_POST['stripe_token'] );
364
+ $maybe_saved_card = ! isset( $_POST['wc-stripe-new-payment-method'] ) || ! empty( $_POST['wc-stripe-new-payment-method'] );
365
+
366
+ // This is true if the user wants to store the card to their account.
367
+ if ( ( $user_id && $this->saved_cards && $maybe_saved_card ) || $force_customer ) {
368
+ $stripe_source = $stripe_customer->add_card( $stripe_token );
369
+
370
+ if ( is_wp_error( $stripe_source ) ) {
371
+ throw new Exception( $stripe_source->get_error_message() );
372
+ }
373
+
374
+ } else {
375
+ // Not saving token, so don't define customer either.
376
+ $stripe_source = $stripe_token;
377
+ $stripe_customer = false;
378
+ }
379
+ }
380
+
381
+ // Use an existing token, and then process the payment
382
+ elseif ( isset( $_POST['wc-stripe-payment-token'] ) && 'new' !== $_POST['wc-stripe-payment-token'] ) {
383
+ $stripe_source = wc_clean( $_POST['wc-stripe-payment-token'] );
384
+ }
385
+
386
+ return (object) array(
387
+ 'token_id' => $token_id,
388
+ 'customer' => $stripe_customer ? $stripe_customer->get_id() : false,
389
+ 'source' => $stripe_source,
390
+ );
391
+ }
392
+
393
+ /**
394
+ * Get payment source from an order. This could be used in the future for
395
+ * a subscription as an example, therefore using the current user ID would
396
+ * not work - the customer won't be logged in :)
397
+ *
398
+ * Not using 2.6 tokens for this part since we need a customer AND a card
399
+ * token, and not just one.
400
+ *
401
+ * @param object $order
402
+ * @return object
403
+ */
404
+ protected function get_order_source( $order = null ) {
405
+ $stripe_customer = new WC_Stripe_Customer();
406
+ $stripe_source = false;
407
+ $token_id = false;
408
+
409
+ if ( $order ) {
410
+ if ( $meta_value = get_post_meta( $order->id, '_stripe_customer_id', true ) ) {
411
+ $stripe_customer->set_id( $meta_value );
412
+ }
413
+ if ( $meta_value = get_post_meta( $order->id, '_stripe_card_id', true ) ) {
414
+ $stripe_source = $meta_value;
415
+ }
416
+ }
417
+
418
+ return (object) array(
419
+ 'token_id' => $token_id,
420
+ 'customer' => $stripe_customer ? $stripe_customer->get_id() : false,
421
+ 'source' => $stripe_source,
422
+ );
423
+ }
424
+
425
+ /**
426
+ * Process the payment
427
+ */
428
+ public function process_payment( $order_id, $retry = true, $force_customer = false ) {
429
+ try {
430
+ $order = wc_get_order( $order_id );
431
+ $source = $this->get_source( get_current_user_id(), $force_customer );
432
+
433
+ if ( empty( $source->source ) && empty( $source->customer ) ) {
434
+ $error_msg = __( 'Please enter your card details to make a payment.', 'woocommerce-gateway-stripe' );
435
+ $error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce-gateway-stripe' );
436
+ throw new Exception( $error_msg );
437
+ }
438
+
439
+ // Store source to order meta
440
+ $this->save_source( $order, $source );
441
+
442
+ // Handle payment
443
+ if ( $order->get_total() > 0 ) {
444
+
445
+ if ( $order->get_total() * 100 < 50 ) {
446
+ throw new Exception( __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce-gateway-stripe' ) );
447
+ }
448
+
449
+ WC_Stripe::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
450
+
451
+ // Make the request
452
+ $response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
453
+
454
+ if ( is_wp_error( $response ) ) {
455
+ // Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
456
+ if ( 'customer' === $response->get_error_code() && $retry ) {
457
+ delete_user_meta( get_current_user_id(), '_stripe_customer_id' );
458
+ return $this->process_payment( $order_id, false, $force_customer );
459
+ }
460
+ throw new Exception( $response->get_error_code() . ': ' . $response->get_error_message() );
461
+ }
462
+
463
+ // Process valid response
464
+ $this->process_response( $response, $order );
465
+ } else {
466
+ $order->payment_complete();
467
+ }
468
+
469
+ // Remove cart
470
+ WC()->cart->empty_cart();
471
+
472
+ // Return thank you page redirect
473
+ return array(
474
+ 'result' => 'success',
475
+ 'redirect' => $this->get_return_url( $order )
476
+ );
477
+
478
+ } catch ( Exception $e ) {
479
+ wc_add_notice( $e->getMessage(), 'error' );
480
+ WC()->session->set( 'refresh_totals', true );
481
+ WC_Stripe::log( sprintf( __( 'Error: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
482
+ return;
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Save source to order.
488
+ */
489
+ protected function save_source( $order, $source ) {
490
+ // Store source in the order
491
+ if ( $source->customer ) {
492
+ update_post_meta( $order->id, '_stripe_customer_id', $source->customer );
493
+ }
494
+ if ( $source->source ) {
495
+ update_post_meta( $order->id, '_stripe_card_id', $source->source->id );
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Store extra meta data for an order from a Stripe Response.
501
+ */
502
+ public function process_response( $response, $order ) {
503
+ WC_Stripe::log( "Processing response: " . print_r( $response, true ) );
504
+
505
+ // Store charge data
506
+ update_post_meta( $order->id, '_stripe_charge_id', $response->id );
507
+ update_post_meta( $order->id, '_stripe_charge_captured', $response->captured ? 'yes' : 'no' );
508
+
509
+ // Store other data such as fees
510
+ if ( isset( $response->balance_transaction ) && isset( $response->balance_transaction->fee ) ) {
511
+ $fee = number_format( $response->balance_transaction->fee / 100, 2, '.', '' );
512
+ update_post_meta( $order->id, 'Stripe Fee', $fee );
513
+ update_post_meta( $order->id, 'Net Revenue From Stripe', $order->get_total() - $fee );
514
+ }
515
+
516
+ if ( $response->captured ) {
517
+ $order->payment_complete( $response->id );
518
+ WC_Stripe::log( "Successful charge: $response->id" );
519
+ } else {
520
+ add_post_meta( $order->id, '_transaction_id', $response->id, true );
521
+
522
+ if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
523
+ $order->reduce_order_stock();
524
+ }
525
+
526
+ $order->update_status( 'on-hold', sprintf( __( 'Stripe charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization.', 'woocommerce-gateway-stripe' ), $response->id ) );
527
+ WC_Stripe::log( "Successful auth: $response->id" );
528
+ }
529
+
530
+ return $response;
531
+ }
532
+
533
+ /**
534
+ * Add payment method via account screen.
535
+ * We don't store the token locally, but to the Stripe API.
536
+ * @since 3.0.0
537
+ */
538
+ public function add_payment_method() {
539
+ if ( empty( $_POST['stripe_token'] ) || ! is_user_logged_in() ) {
540
+ wc_add_notice( __( 'There was a problem adding the card.', 'woocommerce-gateway-stripe' ), 'error' );
541
+ return;
542
+ }
543
+
544
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
545
+ $result = $stripe_customer->add_card( wc_clean( $_POST['stripe_token'] ) );
546
+
547
+ if ( is_wp_error( $result ) ) {
548
+ throw new Exception( $result->get_error_message() );
549
+ }
550
+
551
+ return array(
552
+ 'result' => 'success',
553
+ 'redirect' => wc_get_endpoint_url( 'payment-methods' ),
554
+ );
555
+ }
556
+
557
+ /**
558
+ * Refund a charge
559
+ * @param int $order_id
560
+ * @param float $amount
561
+ * @return bool
562
+ */
563
+ public function process_refund( $order_id, $amount = null, $reason = '' ) {
564
+ $order = wc_get_order( $order_id );
565
+
566
+ if ( ! $order || ! $order->get_transaction_id() ) {
567
+ return false;
568
+ }
569
+
570
+ $body = array();
571
+
572
+ if ( ! is_null( $amount ) ) {
573
+ $body['amount'] = $this->get_stripe_amount( $amount );
574
+ }
575
+
576
+ if ( $reason ) {
577
+ $body['metadata'] = array(
578
+ 'reason' => $reason,
579
+ );
580
+ }
581
+
582
+ WC_Stripe::log( "Info: Beginning refund for order $order_id for the amount of {$amount}" );
583
+
584
+ $response = WC_Stripe_API::request( $body, 'charges/' . $order->get_transaction_id() . '/refunds' );
585
+
586
+ if ( is_wp_error( $response ) ) {
587
+ WC_Stripe::log( "Error: " . $response->get_error_message() );
588
+ return $response;
589
+ } elseif ( ! empty( $response->id ) ) {
590
+ $refund_message = sprintf( __( 'Refunded %s - Refund ID: %s - Reason: %s', 'woocommerce-gateway-stripe' ), wc_price( $response->amount / 100 ), $response->id, $reason );
591
+ $order->add_order_note( $refund_message );
592
+ WC_Stripe::log( "Success: " . html_entity_decode( strip_tags( $refund_message ) ) );
593
+ return true;
594
+ }
595
+ }
596
+ }
includes/legacy/templates/saved-cards.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h2 id="saved-cards" style="margin-top:40px;"><?php _e( 'Saved cards', 'woocommerce-gateway-stripe' ); ?></h2>
2
+ <table class="shop_table">
3
+ <thead>
4
+ <tr>
5
+ <th><?php esc_html_e( 'Card', 'woocommerce-gateway-stripe' ); ?></th>
6
+ <th><?php esc_html_e( 'Expires', 'woocommerce-gateway-stripe' ); ?></th>
7
+ <th></th>
8
+ </tr>
9
+ </thead>
10
+ <tbody>
11
+ <?php foreach ( $cards as $card ) :
12
+ if ( 'card' !== $card->object ) {
13
+ continue;
14
+ }
15
+
16
+ $is_default_card = $card->id === $default_card ? true : false;
17
+ ?>
18
+ <tr>
19
+ <td><?php printf( __( '%s card ending in %s', 'woocommerce-gateway-stripe' ), $card->brand, $card->last4 ); ?>
20
+ <?php if ( $is_default_card ) echo '<br />' . __( '(Default)', 'woocommerce-gateway-stripe' ); ?>
21
+ </td>
22
+ <td><?php printf( __( 'Expires %s/%s', 'woocommerce-gateway-stripe' ), $card->exp_month, $card->exp_year ); ?></td>
23
+ <td>
24
+ <form action="" method="POST">
25
+ <?php wp_nonce_field ( 'stripe_del_card' ); ?>
26
+ <input type="hidden" name="stripe_delete_card" value="<?php echo esc_attr( $card->id ); ?>">
27
+ <input type="submit" class="button" value="<?php esc_attr_e( 'Delete card', 'woocommerce-gateway-stripe' ); ?>">
28
+ </form>
29
+
30
+ <?php if ( ! $is_default_card ) { ?>
31
+ <form action="" method="POST" style="margin-top:10px;">
32
+ <?php wp_nonce_field ( 'stripe_default_card' ); ?>
33
+ <input type="hidden" name="stripe_default_card" value="<?php echo esc_attr( $card->id ); ?>">
34
+ <input type="submit" class="button" value="<?php esc_attr_e( 'Make Default', 'woocommerce-gateway-stripe' ); ?>">
35
+ </form>
36
+ <?php } ?>
37
+ </td>
38
+ </tr>
39
+ <?php endforeach; ?>
40
+ </tbody>
41
+ </table>
includes/settings-stripe.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ return apply_filters( 'wc_stripe_settings',
7
+ array(
8
+ 'enabled' => array(
9
+ 'title' => __( 'Enable/Disable', 'woocommerce-gateway-stripe' ),
10
+ 'label' => __( 'Enable Stripe', 'woocommerce-gateway-stripe' ),
11
+ 'type' => 'checkbox',
12
+ 'description' => '',
13
+ 'default' => 'no'
14
+ ),
15
+ 'title' => array(
16
+ 'title' => __( 'Title', 'woocommerce-gateway-stripe' ),
17
+ 'type' => 'text',
18
+ 'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-gateway-stripe' ),
19
+ 'default' => __( 'Credit card (Stripe)', 'woocommerce-gateway-stripe' ),
20
+ 'desc_tip' => true,
21
+ ),
22
+ 'description' => array(
23
+ 'title' => __( 'Description', 'woocommerce-gateway-stripe' ),
24
+ 'type' => 'text',
25
+ 'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-gateway-stripe' ),
26
+ 'default' => __( 'Pay with your credit card via Stripe.', 'woocommerce-gateway-stripe'),
27
+ 'desc_tip' => true,
28
+ ),
29
+ 'testmode' => array(
30
+ 'title' => __( 'Test mode', 'woocommerce-gateway-stripe' ),
31
+ 'label' => __( 'Enable Test Mode', 'woocommerce-gateway-stripe' ),
32
+ 'type' => 'checkbox',
33
+ 'description' => __( 'Place the payment gateway in test mode using test API keys.', 'woocommerce-gateway-stripe' ),
34
+ 'default' => 'yes',
35
+ 'desc_tip' => true,
36
+ ),
37
+ 'secret_key' => array(
38
+ 'title' => __( 'Live Secret Key', 'woocommerce-gateway-stripe' ),
39
+ 'type' => 'text',
40
+ 'description' => __( 'Get your API keys from your stripe account.', 'woocommerce-gateway-stripe' ),
41
+ 'default' => '',
42
+ 'desc_tip' => true,
43
+ ),
44
+ 'publishable_key' => array(
45
+ 'title' => __( 'Live Publishable Key', 'woocommerce-gateway-stripe' ),
46
+ 'type' => 'text',
47
+ 'description' => __( 'Get your API keys from your stripe account.', 'woocommerce-gateway-stripe' ),
48
+ 'default' => '',
49
+ 'desc_tip' => true,
50
+ ),
51
+ 'test_secret_key' => array(
52
+ 'title' => __( 'Test Secret Key', 'woocommerce-gateway-stripe' ),
53
+ 'type' => 'text',
54
+ 'description' => __( 'Get your API keys from your stripe account.', 'woocommerce-gateway-stripe' ),
55
+ 'default' => '',
56
+ 'desc_tip' => true,
57
+ ),
58
+ 'test_publishable_key' => array(
59
+ 'title' => __( 'Test Publishable Key', 'woocommerce-gateway-stripe' ),
60
+ 'type' => 'text',
61
+ 'description' => __( 'Get your API keys from your stripe account.', 'woocommerce-gateway-stripe' ),
62
+ 'default' => '',
63
+ 'desc_tip' => true,
64
+ ),
65
+ 'capture' => array(
66
+ 'title' => __( 'Capture', 'woocommerce-gateway-stripe' ),
67
+ 'label' => __( 'Capture charge immediately', 'woocommerce-gateway-stripe' ),
68
+ 'type' => 'checkbox',
69
+ 'description' => __( 'Whether or not to immediately capture the charge. When unchecked, the charge issues an authorization and will need to be captured later. Uncaptured charges expire in 7 days.', 'woocommerce-gateway-stripe' ),
70
+ 'default' => 'yes',
71
+ 'desc_tip' => true,
72
+ ),
73
+ 'stripe_checkout' => array(
74
+ 'title' => __( 'Stripe Checkout', 'woocommerce-gateway-stripe' ),
75
+ 'label' => __( 'Enable Stripe Checkout', 'woocommerce-gateway-stripe' ),
76
+ 'type' => 'checkbox',
77
+ 'description' => __( 'If enabled, this option shows a "pay" button and modal credit card form on the checkout, instead of credit card fields directly on the page.', 'woocommerce-gateway-stripe' ),
78
+ 'default' => 'no',
79
+ 'desc_tip' => true,
80
+ ),
81
+ 'stripe_checkout_locale' => array(
82
+ 'title' => __( 'Stripe Checkout locale', 'woocommerce-gateway-stripe' ),
83
+ 'type' => 'select',
84
+ 'class' => 'wc-enhanced-select',
85
+ 'description' => __( 'Language to display in Stripe Checkout modal. Specify Auto to display Checkout in the user\'s preferred language, if available. English will be used by default.', 'woocommerce-gateway-stripe' ),
86
+ 'default' => 'en',
87
+ 'desc_tip' => true,
88
+ 'options' => array(
89
+ 'auto' => __( 'Auto', 'woocommerce-gateway-stripe' ),
90
+ 'zh' => __( 'Simplified Chinese', 'woocommerce-gateway-stripe' ),
91
+ 'nl' => __( 'Dutch', 'woocommerce-gateway-stripe' ),
92
+ 'en' => __( 'English', 'woocommerce-gateway-stripe' ),
93
+ 'fr' => __( 'French', 'woocommerce-gateway-stripe' ),
94
+ 'de' => __( 'German', 'woocommerce-gateway-stripe' ),
95
+ 'it' => __( 'Italian', 'woocommerce-gateway-stripe' ),
96
+ 'ja' => __( 'Japanese', 'woocommerce-gateway-stripe' ),
97
+ 'es' => __( 'Spanish', 'woocommerce-gateway-stripe' ),
98
+ ),
99
+ ),
100
+ 'stripe_bitcoin' => array(
101
+ 'title' => __( 'Bitcoin Currency', 'woocommerce-gateway-stripe' ),
102
+ 'label' => __( 'Enable Bitcoin Currency', 'woocommerce-gateway-stripe' ),
103
+ 'type' => 'checkbox',
104
+ 'description' => __( 'If enabled, an option to accept bitcoin will show on the checkout modal. Note: Stripe Checkout needs to be enabled and store currency must be set to USD.', 'woocommerce-gateway-stripe' ),
105
+ 'default' => 'no',
106
+ 'desc_tip' => true,
107
+ ),
108
+ 'stripe_checkout_image' => array(
109
+ 'title' => __( 'Stripe Checkout Image', 'woocommerce-gateway-stripe' ),
110
+ 'description' => __( 'Optionally enter the URL to a 128x128px image of your brand or product. e.g. <code>https://yoursite.com/wp-content/uploads/2013/09/yourimage.jpg</code>', 'woocommerce-gateway-stripe' ),
111
+ 'type' => 'text',
112
+ 'default' => '',
113
+ 'desc_tip' => true,
114
+ ),
115
+ 'saved_cards' => array(
116
+ 'title' => __( 'Saved Cards', 'woocommerce-gateway-stripe' ),
117
+ 'label' => __( 'Enable Payment via Saved Cards', 'woocommerce-gateway-stripe' ),
118
+ 'type' => 'checkbox',
119
+ 'description' => __( 'If enabled, users will be able to pay with a saved card during checkout. Card details are saved on Stripe servers, not on your store.', 'woocommerce-gateway-stripe' ),
120
+ 'default' => 'no',
121
+ 'desc_tip' => true,
122
+ ),
123
+ 'logging' => array(
124
+ 'title' => __( 'Logging', 'woocommerce-gateway-stripe' ),
125
+ 'label' => __( 'Log debug messages', 'woocommerce-gateway-stripe' ),
126
+ 'type' => 'checkbox',
127
+ 'description' => __( 'Save debug messages to the WooCommerce System Status log.', 'woocommerce-gateway-stripe' ),
128
+ 'default' => 'no',
129
+ 'desc_tip' => true,
130
+ ),
131
+ )
132
+ );
languages/index.html ADDED
File without changes
languages/woocommerce-gateway-stripe.pot ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2016 WooCommerce Stripe Gateway
2
+ # This file is distributed under the same license as the WooCommerce Stripe Gateway package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: WooCommerce Stripe Gateway 3.0.0\n"
6
+ "Report-Msgid-Bugs-To: http://wordpress.org/tag/woocommerce-gateway-stripe\n"
7
+ "POT-Creation-Date: 2016-06-07 10:40:14+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+
15
+ #: includes/class-wc-gateway-stripe-addons.php:104
16
+ #: includes/class-wc-gateway-stripe-addons.php:150
17
+ #: includes/class-wc-gateway-stripe.php:415
18
+ #: includes/legacy/class-wc-gateway-stripe.php:446
19
+ msgid ""
20
+ "Sorry, the minimum allowed order total is 0.50 to use this payment method."
21
+ msgstr ""
22
+
23
+ #: includes/class-wc-gateway-stripe-addons.php:117
24
+ msgid "Customer not found"
25
+ msgstr ""
26
+
27
+ #: includes/class-wc-gateway-stripe-addons.php:157
28
+ msgid "Unable to store payment details. Please try again."
29
+ msgstr ""
30
+
31
+ #: includes/class-wc-gateway-stripe-addons.php:218
32
+ #: includes/class-wc-gateway-stripe-addons.php:269
33
+ msgid "Stripe Transaction Failed (%s)"
34
+ msgstr ""
35
+
36
+ #: includes/class-wc-gateway-stripe-addons.php:400
37
+ #: includes/class-wc-gateway-stripe-addons.php:405
38
+ msgid "Via %s card ending in %s"
39
+ msgstr ""
40
+
41
+ #: includes/class-wc-gateway-stripe.php:18
42
+ #: includes/legacy/class-wc-gateway-stripe.php:18
43
+ msgid "Stripe"
44
+ msgstr ""
45
+
46
+ #: includes/class-wc-gateway-stripe.php:19
47
+ #: includes/legacy/class-wc-gateway-stripe.php:19
48
+ msgid ""
49
+ "Stripe works by adding credit card fields on the checkout and then sending "
50
+ "the details to Stripe for verification."
51
+ msgstr ""
52
+
53
+ #: includes/class-wc-gateway-stripe.php:61
54
+ #: includes/legacy/class-wc-gateway-stripe.php:60
55
+ msgid "Continue to payment"
56
+ msgstr ""
57
+
58
+ #: includes/class-wc-gateway-stripe.php:65
59
+ #: includes/legacy/class-wc-gateway-stripe.php:64
60
+ msgid ""
61
+ "TEST MODE ENABLED. In test mode, you can use the card number "
62
+ "4242424242424242 with any CVC and a valid expiration date or check the "
63
+ "documentation \"<a href=\"%s\">Testing Stripe</a>\" for more card numbers."
64
+ msgstr ""
65
+
66
+ #: includes/class-wc-gateway-stripe.php:148
67
+ #: includes/legacy/class-wc-gateway-stripe.php:165
68
+ msgid ""
69
+ "Stripe is enabled, but the <a href=\"%s\">force SSL option</a> is disabled; "
70
+ "your checkout may not be secure! Please enable SSL and ensure your server "
71
+ "has a valid SSL certificate - Stripe will only work in test mode."
72
+ msgstr ""
73
+
74
+ #: includes/class-wc-gateway-stripe.php:202
75
+ msgid "Add Card"
76
+ msgstr ""
77
+
78
+ #: includes/class-wc-gateway-stripe.php:213
79
+ #: includes/legacy/class-wc-gateway-stripe.php:262
80
+ msgid "%s"
81
+ msgstr ""
82
+
83
+ #: includes/class-wc-gateway-stripe.php:257
84
+ #: includes/legacy/class-wc-gateway-stripe.php:296
85
+ msgid "Please accept the terms and conditions first"
86
+ msgstr ""
87
+
88
+ #: includes/class-wc-gateway-stripe.php:258
89
+ #: includes/legacy/class-wc-gateway-stripe.php:297
90
+ msgid "Please fill in required checkout fields first"
91
+ msgstr ""
92
+
93
+ #: includes/class-wc-gateway-stripe.php:292
94
+ #: includes/legacy/class-wc-gateway-stripe.php:331
95
+ msgid "%s - Order %s"
96
+ msgstr ""
97
+
98
+ #: includes/class-wc-gateway-stripe.php:349
99
+ msgid "Invalid payment method. Please input a new card number."
100
+ msgstr ""
101
+
102
+ #: includes/class-wc-gateway-stripe.php:403
103
+ #: includes/legacy/class-wc-gateway-stripe.php:434
104
+ msgid "Please enter your card details to make a payment."
105
+ msgstr ""
106
+
107
+ #: includes/class-wc-gateway-stripe.php:404
108
+ #: includes/legacy/class-wc-gateway-stripe.php:435
109
+ msgid ""
110
+ "Developers: Please make sure that you are including jQuery and there are no "
111
+ "JavaScript errors on the page."
112
+ msgstr ""
113
+
114
+ #: includes/class-wc-gateway-stripe.php:432
115
+ msgid "This card is no longer available and has been removed."
116
+ msgstr ""
117
+
118
+ #: includes/class-wc-gateway-stripe.php:455
119
+ #: includes/legacy/class-wc-gateway-stripe.php:481
120
+ msgid "Error: %s"
121
+ msgstr ""
122
+
123
+ #: includes/class-wc-gateway-stripe.php:500
124
+ #: includes/legacy/class-wc-gateway-stripe.php:526
125
+ msgid ""
126
+ "Stripe charge authorized (Charge ID: %s). Process order to take payment, or "
127
+ "cancel to remove the pre-authorization."
128
+ msgstr ""
129
+
130
+ #: includes/class-wc-gateway-stripe.php:514
131
+ #: includes/legacy/class-wc-gateway-stripe.php:540
132
+ msgid "There was a problem adding the card."
133
+ msgstr ""
134
+
135
+ #: includes/class-wc-gateway-stripe.php:564
136
+ #: includes/legacy/class-wc-gateway-stripe.php:590
137
+ msgid "Refunded %s - Refund ID: %s - Reason: %s"
138
+ msgstr ""
139
+
140
+ #: includes/class-wc-stripe-api.php:73
141
+ msgid "There was a problem connecting to the payment gateway."
142
+ msgstr ""
143
+
144
+ #: includes/class-wc-stripe-customer.php:143
145
+ msgid "Could not create Stripe customer."
146
+ msgstr ""
147
+
148
+ #: includes/class-wc-stripe-customer.php:184
149
+ msgid "Unable to add card"
150
+ msgstr ""
151
+
152
+ #: includes/legacy/class-wc-gateway-stripe-saved-cards.php:50
153
+ #: includes/legacy/class-wc-gateway-stripe-saved-cards.php:73
154
+ msgid "Unable to make default card, please try again"
155
+ msgstr ""
156
+
157
+ #: includes/legacy/class-wc-gateway-stripe-saved-cards.php:54
158
+ msgid "Unable to delete card."
159
+ msgstr ""
160
+
161
+ #: includes/legacy/class-wc-gateway-stripe-saved-cards.php:56
162
+ msgid "Card deleted."
163
+ msgstr ""
164
+
165
+ #: includes/legacy/class-wc-gateway-stripe-saved-cards.php:77
166
+ msgid "Unable to update default card."
167
+ msgstr ""
168
+
169
+ #: includes/legacy/class-wc-gateway-stripe-saved-cards.php:79
170
+ msgid "Default card updated."
171
+ msgstr ""
172
+
173
+ #: includes/legacy/class-wc-gateway-stripe.php:149
174
+ msgid "Stripe error: Please enter your secret key <a href=\"%s\">here</a>"
175
+ msgstr ""
176
+
177
+ #: includes/legacy/class-wc-gateway-stripe.php:153
178
+ msgid "Stripe error: Please enter your publishable key <a href=\"%s\">here</a>"
179
+ msgstr ""
180
+
181
+ #: includes/legacy/class-wc-gateway-stripe.php:159
182
+ msgid ""
183
+ "Stripe error: Your secret and publishable keys match. Please check and re-"
184
+ "enter."
185
+ msgstr ""
186
+
187
+ #: includes/legacy/class-wc-gateway-stripe.php:218
188
+ msgid "Manage cards"
189
+ msgstr ""
190
+
191
+ #: includes/legacy/class-wc-gateway-stripe.php:229
192
+ msgid "%s card ending in %s (Expires %s/%s)"
193
+ msgstr ""
194
+
195
+ #: includes/legacy/class-wc-gateway-stripe.php:237
196
+ msgid "Use a new credit card"
197
+ msgstr ""
198
+
199
+ #: includes/legacy/templates/saved-cards.php:1
200
+ msgid "Saved cards"
201
+ msgstr ""
202
+
203
+ #: includes/legacy/templates/saved-cards.php:5
204
+ msgid "Card"
205
+ msgstr ""
206
+
207
+ #: includes/legacy/templates/saved-cards.php:6
208
+ msgid "Expires"
209
+ msgstr ""
210
+
211
+ #: includes/legacy/templates/saved-cards.php:19
212
+ msgid "%s card ending in %s"
213
+ msgstr ""
214
+
215
+ #: includes/legacy/templates/saved-cards.php:20
216
+ msgid "(Default)"
217
+ msgstr ""
218
+
219
+ #: includes/legacy/templates/saved-cards.php:22
220
+ msgid "Expires %s/%s"
221
+ msgstr ""
222
+
223
+ #: includes/legacy/templates/saved-cards.php:27
224
+ msgid "Delete card"
225
+ msgstr ""
226
+
227
+ #: includes/legacy/templates/saved-cards.php:34
228
+ msgid "Make Default"
229
+ msgstr ""
230
+
231
+ #: includes/settings-stripe.php:9
232
+ msgid "Enable/Disable"
233
+ msgstr ""
234
+
235
+ #: includes/settings-stripe.php:10
236
+ msgid "Enable Stripe"
237
+ msgstr ""
238
+
239
+ #: includes/settings-stripe.php:16
240
+ msgid "Title"
241
+ msgstr ""
242
+
243
+ #: includes/settings-stripe.php:18
244
+ msgid "This controls the title which the user sees during checkout."
245
+ msgstr ""
246
+
247
+ #: includes/settings-stripe.php:19
248
+ msgid "Credit card (Stripe)"
249
+ msgstr ""
250
+
251
+ #: includes/settings-stripe.php:23
252
+ msgid "Description"
253
+ msgstr ""
254
+
255
+ #: includes/settings-stripe.php:25
256
+ msgid "This controls the description which the user sees during checkout."
257
+ msgstr ""
258
+
259
+ #: includes/settings-stripe.php:26
260
+ msgid "Pay with your credit card via Stripe."
261
+ msgstr ""
262
+
263
+ #: includes/settings-stripe.php:30
264
+ msgid "Test mode"
265
+ msgstr ""
266
+
267
+ #: includes/settings-stripe.php:31
268
+ msgid "Enable Test Mode"
269
+ msgstr ""
270
+
271
+ #: includes/settings-stripe.php:33
272
+ msgid "Place the payment gateway in test mode using test API keys."
273
+ msgstr ""
274
+
275
+ #: includes/settings-stripe.php:38
276
+ msgid "Live Secret Key"
277
+ msgstr ""
278
+
279
+ #: includes/settings-stripe.php:40 includes/settings-stripe.php:47
280
+ #: includes/settings-stripe.php:54 includes/settings-stripe.php:61
281
+ msgid "Get your API keys from your stripe account."
282
+ msgstr ""
283
+
284
+ #: includes/settings-stripe.php:45
285
+ msgid "Live Publishable Key"
286
+ msgstr ""
287
+
288
+ #: includes/settings-stripe.php:52
289
+ msgid "Test Secret Key"
290
+ msgstr ""
291
+
292
+ #: includes/settings-stripe.php:59
293
+ msgid "Test Publishable Key"
294
+ msgstr ""
295
+
296
+ #: includes/settings-stripe.php:66
297
+ msgid "Capture"
298
+ msgstr ""
299
+
300
+ #: includes/settings-stripe.php:67
301
+ msgid "Capture charge immediately"
302
+ msgstr ""
303
+
304
+ #: includes/settings-stripe.php:69
305
+ msgid ""
306
+ "Whether or not to immediately capture the charge. When unchecked, the charge "
307
+ "issues an authorization and will need to be captured later. Uncaptured "
308
+ "charges expire in 7 days."
309
+ msgstr ""
310
+
311
+ #: includes/settings-stripe.php:74
312
+ msgid "Stripe Checkout"
313
+ msgstr ""
314
+
315
+ #: includes/settings-stripe.php:75
316
+ msgid "Enable Stripe Checkout"
317
+ msgstr ""
318
+
319
+ #: includes/settings-stripe.php:77
320
+ msgid ""
321
+ "If enabled, this option shows a \"pay\" button and modal credit card form on "
322
+ "the checkout, instead of credit card fields directly on the page."
323
+ msgstr ""
324
+
325
+ #: includes/settings-stripe.php:82
326
+ msgid "Stripe Checkout locale"
327
+ msgstr ""
328
+
329
+ #: includes/settings-stripe.php:85
330
+ msgid ""
331
+ "Language to display in Stripe Checkout modal. Specify Auto to display "
332
+ "Checkout in the user's preferred language, if available. English will be "
333
+ "used by default."
334
+ msgstr ""
335
+
336
+ #: includes/settings-stripe.php:89
337
+ msgid "Auto"
338
+ msgstr ""
339
+
340
+ #: includes/settings-stripe.php:90
341
+ msgid "Simplified Chinese"
342
+ msgstr ""
343
+
344
+ #: includes/settings-stripe.php:91
345
+ msgid "Dutch"
346
+ msgstr ""
347
+
348
+ #: includes/settings-stripe.php:92
349
+ msgid "English"
350
+ msgstr ""
351
+
352
+ #: includes/settings-stripe.php:93
353
+ msgid "French"
354
+ msgstr ""
355
+
356
+ #: includes/settings-stripe.php:94
357
+ msgid "German"
358
+ msgstr ""
359
+
360
+ #: includes/settings-stripe.php:95
361
+ msgid "Italian"
362
+ msgstr ""
363
+
364
+ #: includes/settings-stripe.php:96
365
+ msgid "Japanese"
366
+ msgstr ""
367
+
368
+ #: includes/settings-stripe.php:97
369
+ msgid "Spanish"
370
+ msgstr ""
371
+
372
+ #: includes/settings-stripe.php:101
373
+ msgid "Bitcoin Currency"
374
+ msgstr ""
375
+
376
+ #: includes/settings-stripe.php:102
377
+ msgid "Enable Bitcoin Currency"
378
+ msgstr ""
379
+
380
+ #: includes/settings-stripe.php:104
381
+ msgid ""
382
+ "If enabled, an option to accept bitcoin will show on the checkout modal. "
383
+ "Note: Stripe Checkout needs to be enabled and store currency must be set to "
384
+ "USD."
385
+ msgstr ""
386
+
387
+ #: includes/settings-stripe.php:109
388
+ msgid "Stripe Checkout Image"
389
+ msgstr ""
390
+
391
+ #: includes/settings-stripe.php:110
392
+ msgid ""
393
+ "Optionally enter the URL to a 128x128px image of your brand or product. e.g. "
394
+ "<code>https://yoursite.com/wp-content/uploads/2013/09/yourimage.jpg</code>"
395
+ msgstr ""
396
+
397
+ #: includes/settings-stripe.php:116
398
+ msgid "Saved Cards"
399
+ msgstr ""
400
+
401
+ #: includes/settings-stripe.php:117
402
+ msgid "Enable Payment via Saved Cards"
403
+ msgstr ""
404
+
405
+ #: includes/settings-stripe.php:119
406
+ msgid ""
407
+ "If enabled, users will be able to pay with a saved card during checkout. "
408
+ "Card details are saved on Stripe servers, not on your store."
409
+ msgstr ""
410
+
411
+ #: includes/settings-stripe.php:124
412
+ msgid "Logging"
413
+ msgstr ""
414
+
415
+ #: includes/settings-stripe.php:125
416
+ msgid "Log debug messages"
417
+ msgstr ""
418
+
419
+ #: includes/settings-stripe.php:127
420
+ msgid "Save debug messages to the WooCommerce System Status log."
421
+ msgstr ""
422
+
423
+ #: woocommerce-gateway-stripe.php:173
424
+ msgid ""
425
+ "Stripe is almost ready. To get started, <a href=\"%s\">set your Stripe "
426
+ "account keys</a>."
427
+ msgstr ""
428
+
429
+ #: woocommerce-gateway-stripe.php:184
430
+ msgid ""
431
+ "The plugin could not be activated. The minimum PHP version required for this "
432
+ "plugin is %1$s. You are running %2$s."
433
+ msgstr ""
434
+
435
+ #: woocommerce-gateway-stripe.php:186
436
+ msgid ""
437
+ "The WooCommerce Stripe plugin has been deactivated. The minimum PHP version "
438
+ "required for this plugin is %1$s. You are running %2$s."
439
+ msgstr ""
440
+
441
+ #: woocommerce-gateway-stripe.php:193
442
+ msgid ""
443
+ "The plugin could not be activated. The minimum WooCommerce version required "
444
+ "for this plugin is %1$s. You are running %2$s."
445
+ msgstr ""
446
+
447
+ #: woocommerce-gateway-stripe.php:195
448
+ msgid ""
449
+ "The WooCommerce Stripe plugin has been deactivated. The minimum WooCommerce "
450
+ "version required for this plugin is %1$s. You are running %2$s."
451
+ msgstr ""
452
+
453
+ #: woocommerce-gateway-stripe.php:202
454
+ msgid "The plugin could not be activated. cURL is not installed."
455
+ msgstr ""
456
+
457
+ #: woocommerce-gateway-stripe.php:204
458
+ msgid ""
459
+ "The WooCommerce Stripe plugin has been deactivated. cURL is not installed."
460
+ msgstr ""
461
+
462
+ #: woocommerce-gateway-stripe.php:219
463
+ msgid "Settings"
464
+ msgstr ""
465
+
466
+ #: woocommerce-gateway-stripe.php:220
467
+ msgid "Docs"
468
+ msgstr ""
469
+
470
+ #: woocommerce-gateway-stripe.php:221
471
+ msgid "Support"
472
+ msgstr ""
473
+
474
+ #: woocommerce-gateway-stripe.php:314
475
+ msgid "Unable to capture charge!"
476
+ msgstr ""
477
+
478
+ #: woocommerce-gateway-stripe.php:316
479
+ msgid "Stripe charge complete (Charge ID: %s)"
480
+ msgstr ""
481
+
482
+ #: woocommerce-gateway-stripe.php:348
483
+ msgid "Unable to refund charge!"
484
+ msgstr ""
485
+
486
+ #: woocommerce-gateway-stripe.php:350
487
+ msgid "Stripe charge refunded (Charge ID: %s)"
488
+ msgstr ""
489
+
490
+ #. Plugin Name of the plugin/theme
491
+ msgid "WooCommerce Stripe Gateway"
492
+ msgstr ""
493
+
494
+ #. Plugin URI of the plugin/theme
495
+ msgid "https://wordpress.org/plugins/woocommerce-gateway-stripe/"
496
+ msgstr ""
497
+
498
+ #. Description of the plugin/theme
499
+ msgid "Take credit card payments on your store using Stripe."
500
+ msgstr ""
501
+
502
+ #. Author of the plugin/theme
503
+ msgid "Automattic"
504
+ msgstr ""
505
+
506
+ #. Author URI of the plugin/theme
507
+ msgid "http://woothemes.com/"
508
+ msgstr ""
readme.txt ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WooCommerce Stripe Payment Gateway ===
2
+ Contributors: automattic, woothemes, mikejolley, akeda, royho
3
+ Tags: credit card, stripe, woocommerce
4
+ Requires at least: 4.4
5
+ Tested up to: 4.5
6
+ Stable tag: 3.0.0
7
+ License: GPLv3
8
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
+
10
+ Take credit card payments on your store using Stripe.
11
+
12
+ == Description ==
13
+
14
+ Accept Visa, MasterCard, American Express, Discover, JCB, Diners Club, and more cards directly on your store with the Stripe payment gateway for WooCommerce.
15
+
16
+ = Take Credit card payments easily and directly on your store =
17
+
18
+ The Stripe plugin extends WooCommerce allowing you to take payments directly on your store via Stripe’s API.
19
+
20
+ Stripe is available in:
21
+
22
+ * United States
23
+ * Canada
24
+ * UK
25
+ * Australia
26
+ * Belgium
27
+ * France
28
+ * Germany
29
+ * Ireland
30
+ * The Netherlands
31
+ * [and more](https://support.stripe.com/questions/what-countries-does-stripe-support)
32
+
33
+ Stripe is a simple way to accept payments online. With Stripe you can accept Visa, MasterCard, American Express, Discover, JCB, and Diners Club cards, even Bitcoin, directly on your store.
34
+
35
+ = Why choose Stripe? =
36
+
37
+ Stripe has no setup fees, no monthly fees, no hidden costs: you only get charged when you earn money! Earnings are transferred to your bank account on a 7-day rolling basis.
38
+
39
+ Stripe also supports the [Subscriptions extension](http://www.woothemes.com/extension/subscriptions/) and re-using cards. When a customer pays, they are set up in Stripe as a customer. If they create another order, they can check out using the same card. A massive timesaver for returning customers.
40
+
41
+ == Installation ==
42
+
43
+ Please note, v3 of this gateway requires WooCommerce 2.5 and above.
44
+
45
+ You can download an [older version of this gateway for older versions of WooCommerce from here](https://wordpress.org/plugins/woocommerce-gateway-stripe/developers/).
46
+
47
+ = Automatic installation =
48
+
49
+ Automatic installation is the easiest option as WordPress handles the file transfers itself and you don’t need to leave your web browser. To
50
+ do an automatic install of, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New.
51
+
52
+ In the search field type “WooCommerce Stripe Payment Gateway” and click Search Plugins. Once you’ve found our plugin you can view details about it such as the point release, rating and description. Most importantly of course, you can install it by simply clicking "Install Now".
53
+
54
+ = Manual installation =
55
+
56
+ The manual installation method involves downloading our plugin and uploading it to your web server via your favorite FTP application. The WordPress codex contains [instructions on how to do this here](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
57
+
58
+ = Updating =
59
+
60
+ Automatic updates should work like a charm; as always though, ensure you backup your site just in case.
61
+
62
+ == Frequently Asked Questions ==
63
+
64
+ = Does this support recurring payments, like for subscriptions? =
65
+
66
+ Yes!
67
+
68
+ = Does this support both production mode and sandbox mode for testing? =
69
+
70
+ Yes it does - production and sandbox mode is driven by the API keys you use.
71
+
72
+ = Where can I find documentation? =
73
+
74
+ For help setting up and configuring, please refer to our [user guide](https://docs.woothemes.com/document/stripe/)
75
+
76
+ = Where can I get support or talk to other users? =
77
+
78
+ If you get stuck, you can ask for help in the Plugin Forum.
79
+
80
+ == Screenshots ==
81
+
82
+ 1. The settings panel used to configure the gateway.
83
+ 2. Normal checkout with Stripe.
84
+ 3. Option to save a card to your account.
85
+ 4. Checking out with a saved card.
86
+ 5. Using the Stripe checkout modal.
87
+
88
+ == Changelog ==
89
+
90
+ = 3.0.0 =
91
+ * First public WordPress.org release.
92
+ * Refactor for WC 2.6 and above. Legacy support for 2.5.
93
+ * Improved saved card handling using tokenization API in WooCommerce.
94
+
95
+ [See changelog for all versions](https://raw.githubusercontent.com/woothemes/woocommerce-gateway-stripe/master/CHANGELOG.txt).
woocommerce-gateway-stripe.php ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Plugin Name: WooCommerce Stripe Gateway
4
+ * Plugin URI: https://wordpress.org/plugins/woocommerce-gateway-stripe/
5
+ * Description: Take credit card payments on your store using Stripe.
6
+ * Author: Automattic
7
+ * Author URI: http://woothemes.com/
8
+ * Version: 3.0.0
9
+ *
10
+ * Copyright (c) 2016 Automattic
11
+ *
12
+ * This program is free software: you can redistribute it and/or modify
13
+ * it under the terms of the GNU General Public License as published by
14
+ * the Free Software Foundation, either version 3 of the License, or
15
+ * (at your option) any later version.
16
+ *
17
+ * This program is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
+ */
25
+
26
+ if ( ! defined( 'ABSPATH' ) ) {
27
+ exit;
28
+ }
29
+
30
+ /**
31
+ * Required minimums and constants
32
+ */
33
+ define( 'WC_STRIPE_VERSION', '3.0.0' );
34
+ define( 'WC_STRIPE_MIN_PHP_VER', '5.3.0' );
35
+ define( 'WC_STRIPE_MIN_WC_VER', '2.5.0' );
36
+ define( 'WC_STRIPE_MAIN_FILE', __FILE__ );
37
+ define( 'WC_STRIPE_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
38
+
39
+ if ( ! class_exists( 'WC_Stripe' ) ) {
40
+
41
+ class WC_Stripe {
42
+
43
+ /**
44
+ * @var Singleton The reference the *Singleton* instance of this class
45
+ */
46
+ private static $instance;
47
+
48
+ /**
49
+ * @var Reference to logging class.
50
+ */
51
+ private static $log;
52
+
53
+ /**
54
+ * Returns the *Singleton* instance of this class.
55
+ *
56
+ * @return Singleton The *Singleton* instance.
57
+ */
58
+ public static function get_instance() {
59
+ if ( null === self::$instance ) {
60
+ self::$instance = new self();
61
+ }
62
+ return self::$instance;
63
+ }
64
+
65
+ /**
66
+ * Private clone method to prevent cloning of the instance of the
67
+ * *Singleton* instance.
68
+ *
69
+ * @return void
70
+ */
71
+ private function __clone() {}
72
+
73
+ /**
74
+ * Private unserialize method to prevent unserializing of the *Singleton*
75
+ * instance.
76
+ *
77
+ * @return void
78
+ */
79
+ private function __wakeup() {}
80
+
81
+ /** @var whether or not we need to load code for / support subscriptions */
82
+ private $subscription_support_enabled = false;
83
+
84
+ /**
85
+ * Notices (array)
86
+ * @var array
87
+ */
88
+ public $notices = array();
89
+
90
+ /**
91
+ * Protected constructor to prevent creating a new instance of the
92
+ * *Singleton* via the `new` operator from outside of this class.
93
+ */
94
+ protected function __construct() {
95
+ add_action( 'admin_init', array( $this, 'check_environment' ) );
96
+ add_action( 'admin_notices', array( $this, 'admin_notices' ), 15 );
97
+ add_action( 'plugins_loaded', array( $this, 'init' ) );
98
+ }
99
+
100
+ /**
101
+ * Init the plugin after plugins_loaded so environment variables are set.
102
+ */
103
+ public function init() {
104
+ // Don't hook anything else in the plugin if we're in an incompatible environment
105
+ if ( self::get_environment_warning() ) {
106
+ return;
107
+ }
108
+
109
+ include_once( plugin_basename( 'includes/class-wc-stripe-api.php' ) );
110
+ include_once( plugin_basename( 'includes/class-wc-stripe-customer.php' ) );
111
+
112
+ // Init the gateway itself
113
+ $this->init_gateways();
114
+
115
+ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
116
+ add_action( 'woocommerce_order_status_on-hold_to_processing', array( $this, 'capture_payment' ) );
117
+ add_action( 'woocommerce_order_status_on-hold_to_completed', array( $this, 'capture_payment' ) );
118
+ add_action( 'woocommerce_order_status_on-hold_to_cancelled', array( $this, 'cancel_payment' ) );
119
+ add_action( 'woocommerce_order_status_on-hold_to_refunded', array( $this, 'cancel_payment' ) );
120
+ add_filter( 'woocommerce_get_customer_payment_tokens', array( $this, 'woocommerce_get_customer_payment_tokens' ), 10, 3 );
121
+ add_action( 'woocommerce_payment_token_deleted', array( $this, 'woocommerce_payment_token_deleted' ), 10, 2 );
122
+ add_action( 'woocommerce_payment_token_set_default', array( $this, 'woocommerce_payment_token_set_default' ) );
123
+ }
124
+
125
+ /**
126
+ * Allow this class and other classes to add slug keyed notices (to avoid duplication)
127
+ */
128
+ public function add_admin_notice( $slug, $class, $message ) {
129
+ $this->notices[ $slug ] = array(
130
+ 'class' => $class,
131
+ 'message' => $message
132
+ );
133
+ }
134
+
135
+ /**
136
+ * The primary sanity check, automatically disable the plugin on activation if it doesn't
137
+ * meet minimum requirements.
138
+ *
139
+ * Based on http://wptavern.com/how-to-prevent-wordpress-plugins-from-activating-on-sites-with-incompatible-hosting-environments
140
+ */
141
+ public static function activation_check() {
142
+ $environment_warning = self::get_environment_warning( true );
143
+ if ( $environment_warning ) {
144
+ deactivate_plugins( plugin_basename( __FILE__ ) );
145
+ wp_die( $environment_warning );
146
+ }
147
+ }
148
+
149
+ /**
150
+ * The backup sanity check, in case the plugin is activated in a weird way,
151
+ * or the environment changes after activation.
152
+ */
153
+ public function check_environment() {
154
+ $environment_warning = self::get_environment_warning();
155
+ if ( $environment_warning && is_plugin_active( plugin_basename( __FILE__ ) ) ) {
156
+ deactivate_plugins( plugin_basename( __FILE__ ) );
157
+ $this->add_admin_notice( 'bad_environment', 'error', $environment_warning );
158
+ if ( isset( $_GET['activate'] ) ) {
159
+ unset( $_GET['activate'] );
160
+ }
161
+ }
162
+
163
+ // Check if secret key present. Otherwise prompt, via notice, to go to
164
+ // setting.
165
+ if ( ! class_exists( 'WC_Stripe_API' ) ) {
166
+ include_once( plugin_basename( 'includes/class-wc-stripe-api.php' ) );
167
+ }
168
+
169
+ $secret = WC_Stripe_API::get_secret_key();
170
+
171
+ if ( empty( $secret ) && ! ( isset( $_GET['page'], $_GET['section'] ) && 'wc-settings' === $_GET['page'] && 'stripe' === $_GET['section'] ) ) {
172
+ $setting_link = $this->get_setting_link();
173
+ $this->add_admin_notice( 'prompt_connect', 'notice notice-warning', sprintf( __( 'Stripe is almost ready. To get started, <a href="%s">set your Stripe account keys</a>.', 'wwoocommerce-gateway-stripe' ), $setting_link ) );
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Checks the environment for compatibility problems. Returns a string with the first incompatibility
179
+ * found or false if the environment has no problems.
180
+ */
181
+ static function get_environment_warning( $during_activation = false ) {
182
+ if ( version_compare( phpversion(), WC_STRIPE_MIN_PHP_VER, '<' ) ) {
183
+ if ( $during_activation ) {
184
+ $message = __( 'The plugin could not be activated. The minimum PHP version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe', 'woocommerce-gateway-stripe' );
185
+ } else {
186
+ $message = __( 'The WooCommerce Stripe plugin has been deactivated. The minimum PHP version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe' );
187
+ }
188
+ return sprintf( $message, WC_STRIPE_MIN_PHP_VER, phpversion() );
189
+ }
190
+
191
+ if ( version_compare( WC_VERSION, WC_STRIPE_MIN_WC_VER, '<' ) ) {
192
+ if ( $during_activation ) {
193
+ $message = __( 'The plugin could not be activated. The minimum WooCommerce version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe', 'woocommerce-gateway-stripe' );
194
+ } else {
195
+ $message = __( 'The WooCommerce Stripe plugin has been deactivated. The minimum WooCommerce version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe' );
196
+ }
197
+ return sprintf( $message, WC_STRIPE_MIN_WC_VER, WC_VERSION );
198
+ }
199
+
200
+ if ( ! function_exists( 'curl_init' ) ) {
201
+ if ( $during_activation ) {
202
+ return __( 'The plugin could not be activated. cURL is not installed.', 'woocommerce-gateway-stripe' );
203
+ }
204
+ return __( 'The WooCommerce Stripe plugin has been deactivated. cURL is not installed.', 'woocommerce-gateway-stripe' );
205
+ }
206
+
207
+ return false;
208
+ }
209
+
210
+ /**
211
+ * Adds plugin action links
212
+ *
213
+ * @since 1.0.0
214
+ */
215
+ public function plugin_action_links( $links ) {
216
+ $setting_link = $this->get_setting_link();
217
+
218
+ $plugin_links = array(
219
+ '<a href="' . $setting_link . '">' . __( 'Settings', 'woocommerce-gateway-stripe' ) . '</a>',
220
+ '<a href="https://docs.woothemes.com/document/stripe/">' . __( 'Docs', 'woocommerce-gateway-stripe' ) . '</a>',
221
+ '<a href="http://support.woothemes.com/">' . __( 'Support', 'woocommerce-gateway-stripe' ) . '</a>',
222
+ );
223
+ return array_merge( $plugin_links, $links );
224
+ }
225
+
226
+ /**
227
+ * Get setting link.
228
+ *
229
+ * @since 1.0.0
230
+ *
231
+ * @return string Setting link
232
+ */
233
+ public function get_setting_link() {
234
+ $use_id_as_section = version_compare( WC()->version, '2.6', '>=' );
235
+
236
+ $section_slug = $use_id_as_section ? 'stripe' : strtolower( 'WC_Gateway_Stripe' );
237
+
238
+ return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug );
239
+ }
240
+
241
+ /**
242
+ * Display any notices we've collected thus far (e.g. for connection, disconnection)
243
+ */
244
+ public function admin_notices() {
245
+ foreach ( (array) $this->notices as $notice_key => $notice ) {
246
+ echo "<div class='" . esc_attr( $notice['class'] ) . "'><p>";
247
+ echo wp_kses( $notice['message'], array( 'a' => array( 'href' => array() ) ) );
248
+ echo "</p></div>";
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Initialize the gateway. Called very early - in the context of the plugins_loaded action
254
+ *
255
+ * @since 1.0.0
256
+ */
257
+ public function init_gateways() {
258
+ if ( class_exists( 'WC_Subscriptions_Order' ) && function_exists( 'wcs_create_renewal_order' ) ) {
259
+ $this->subscription_support_enabled = true;
260
+ }
261
+
262
+ if ( ! class_exists( 'WC_Payment_Gateway' ) ) {
263
+ return;
264
+ }
265
+
266
+ if ( class_exists( 'WC_Payment_Gateway_CC' ) ) {
267
+ include_once( plugin_basename( 'includes/class-wc-gateway-stripe.php' ) );
268
+ } else {
269
+ include_once( plugin_basename( 'includes/legacy/class-wc-gateway-stripe.php' ) );
270
+ include_once( plugin_basename( 'includes/legacy/class-wc-gateway-stripe-saved-cards.php' ) );
271
+ }
272
+
273
+ load_plugin_textdomain( 'woocommerce-gateway-stripe', false, trailingslashit( dirname( plugin_basename( __FILE__ ) ) ) );
274
+ add_filter( 'woocommerce_payment_gateways', array( $this, 'add_gateways' ) );
275
+
276
+ if ( $this->subscription_support_enabled ) {
277
+ require_once( plugin_basename( 'includes/class-wc-gateway-stripe-addons.php' ) );
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Add the gateways to WooCommerce
283
+ *
284
+ * @since 1.0.0
285
+ */
286
+ public function add_gateways( $methods ) {
287
+ if ( $this->subscription_support_enabled ) {
288
+ $methods[] = 'WC_Gateway_Stripe_Addons';
289
+ } else {
290
+ $methods[] = 'WC_Gateway_Stripe';
291
+ }
292
+ return $methods;
293
+ }
294
+
295
+ /**
296
+ * Capture payment when the order is changed from on-hold to complete or processing
297
+ *
298
+ * @param int $order_id
299
+ */
300
+ public function capture_payment( $order_id ) {
301
+ $order = wc_get_order( $order_id );
302
+
303
+ if ( 'stripe' === $order->payment_method ) {
304
+ $charge = get_post_meta( $order_id, '_stripe_charge_id', true );
305
+ $captured = get_post_meta( $order_id, '_stripe_charge_captured', true );
306
+
307
+ if ( $charge && 'no' === $captured ) {
308
+ $result = WC_Stripe_API::request( array(
309
+ 'amount' => $order->get_total() * 100,
310
+ 'expand[]' => 'balance_transaction'
311
+ ), 'charges/' . $charge . '/capture' );
312
+
313
+ if ( is_wp_error( $result ) ) {
314
+ $order->add_order_note( __( 'Unable to capture charge!', 'woocommerce-gateway-stripe' ) . ' ' . $result->get_error_message() );
315
+ } else {
316
+ $order->add_order_note( sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $result->id ) );
317
+ update_post_meta( $order->id, '_stripe_charge_captured', 'yes' );
318
+
319
+ // Store other data such as fees
320
+ update_post_meta( $order->id, 'Stripe Payment ID', $result->id );
321
+
322
+ if ( isset( $result->balance_transaction ) && isset( $result->balance_transaction->fee ) ) {
323
+ update_post_meta( $order->id, 'Stripe Fee', number_format( $result->balance_transaction->fee / 100, 2, '.', '' ) );
324
+ update_post_meta( $order->id, 'Net Revenue From Stripe', ( $order->order_total - number_format( $result->balance_transaction->fee / 100, 2, '.', '' ) ) );
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Cancel pre-auth on refund/cancellation
333
+ *
334
+ * @param int $order_id
335
+ */
336
+ public function cancel_payment( $order_id ) {
337
+ $order = wc_get_order( $order_id );
338
+
339
+ if ( 'stripe' === $order->payment_method ) {
340
+ $charge = get_post_meta( $order_id, '_stripe_charge_id', true );
341
+
342
+ if ( $charge ) {
343
+ $result = WC_Stripe_API::request( array(
344
+ 'amount' => $order->get_total() * 100,
345
+ ), 'charges/' . $charge . '/refund' );
346
+
347
+ if ( is_wp_error( $result ) ) {
348
+ $order->add_order_note( __( 'Unable to refund charge!', 'woocommerce-gateway-stripe' ) . ' ' . $result->get_error_message() );
349
+ } else {
350
+ $order->add_order_note( sprintf( __( 'Stripe charge refunded (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $result->id ) );
351
+ delete_post_meta( $order->id, '_stripe_charge_captured' );
352
+ delete_post_meta( $order->id, '_stripe_charge_id' );
353
+ }
354
+ }
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Gets saved tokens from API if they don't already exist in WooCommerce.
360
+ * @param array $tokens
361
+ * @return array
362
+ */
363
+ public function woocommerce_get_customer_payment_tokens( $tokens, $customer_id, $gateway_id ) {
364
+ if ( is_user_logged_in() && 'stripe' === $gateway_id ) {
365
+ $stripe_customer = new WC_Stripe_Customer( $customer_id );
366
+ $stripe_cards = $stripe_customer->get_cards();
367
+ $stored_tokens = array();
368
+
369
+ foreach ( $tokens as $token ) {
370
+ $stored_tokens[] = $token->get_token();
371
+ }
372
+
373
+ foreach ( $stripe_cards as $card ) {
374
+ if ( ! in_array( $card->id, $stored_tokens ) ) {
375
+ $token = new WC_Payment_Token_CC();
376
+ $token->set_token( $card->id );
377
+ $token->set_gateway_id( 'stripe' );
378
+ $token->set_card_type( strtolower( $card->brand ) );
379
+ $token->set_last4( $card->last4 );
380
+ $token->set_expiry_month( $card->exp_month );
381
+ $token->set_expiry_year( $card->exp_year );
382
+ $token->set_user_id( $customer_id );
383
+ $token->save();
384
+ $tokens[ $token->get_id() ] = $token;
385
+ }
386
+ }
387
+ }
388
+ return $tokens;
389
+ }
390
+
391
+ /**
392
+ * Delete token from Stripe
393
+ */
394
+ public function woocommerce_payment_token_deleted( $token_id, $token ) {
395
+ if ( 'stripe' === $token->get_gateway_id() ) {
396
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
397
+ $stripe_customer->delete_card( $token->get_token() );
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Set as default in Stripe
403
+ */
404
+ public function woocommerce_payment_token_set_default( $token_id ) {
405
+ $token = WC_Payment_Tokens::get( $token_id );
406
+ if ( 'stripe' === $token->get_gateway_id() ) {
407
+ $stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
408
+ $stripe_customer->set_default_card( $token->get_token() );
409
+ }
410
+ }
411
+
412
+ /**
413
+ * What rolls down stairs
414
+ * alone or in pairs,
415
+ * and over your neighbor's dog?
416
+ * What's great for a snack,
417
+ * And fits on your back?
418
+ * It's log, log, log
419
+ */
420
+ public static function log( $message ) {
421
+ if ( empty( self::$log ) ) {
422
+ self::$log = new WC_Logger();
423
+ }
424
+
425
+ self::$log->add( 'woocommerce-gateway-stripe', $message );
426
+
427
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
428
+ error_log( $message );
429
+ }
430
+ }
431
+ }
432
+
433
+ $GLOBALS['wc_stripe'] = WC_Stripe::get_instance();
434
+ register_activation_hook( __FILE__, array( 'WC_Stripe', 'activation_check' ) );
435
+
436
+ }
wordpress_org_assets/banner-1544x500.png ADDED
Binary file
wordpress_org_assets/banner-772x250.png ADDED
Binary file
wordpress_org_assets/icon-128x128.png ADDED
Binary file
wordpress_org_assets/icon-256x256.png ADDED
Binary file
wordpress_org_assets/screenshot-1.png ADDED
Binary file
wordpress_org_assets/screenshot-2.png ADDED
Binary file
wordpress_org_assets/screenshot-3.png ADDED
Binary file
wordpress_org_assets/screenshot-4.png ADDED
Binary file
wordpress_org_assets/screenshot-5.png ADDED
Binary file
wordpress_org_assets/wc-stripe.sketch ADDED
Binary file