installed plugin Easy Digital Downloads
version 3.1.0.3
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,399 @@
|
||||
/**
|
||||
* Internal dependencices
|
||||
*/
|
||||
@import './frontend/modal.scss';
|
||||
@import './frontend/payment-request-button.scss';
|
||||
|
||||
#edd_checkout_form_wrap .edd-card-selector-radio label {
|
||||
display: inline;
|
||||
vertical-align: middle;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
font-size: 100%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.edd-card-selector-radio .edd-stripe-card-radio-item {
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid transparent;
|
||||
|
||||
label {
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.add-new-card,
|
||||
.card-label {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edd-card-selector-radio .edd-stripe-card-radio-item.selected {
|
||||
border: 1px solid #f0f0f0;
|
||||
background-color: #fcfcfc;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.edd-card-selector-radio div.card-label {
|
||||
width: 65%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.edd-card-selector-radio span.card-is-default {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.edd-card-selector-radio span.card-expired {
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
.edd-stripe-card-selector + .edd-stripe-new-card {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#edd-stripe-manage-cards .edd-stripe-add-new-card {
|
||||
margin: 20px 0 10px;
|
||||
}
|
||||
|
||||
#edd-stripe-manage-cards div.edd-stripe-card-item {
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
display: inline-grid;
|
||||
border: 1px solid #f0f0f0;
|
||||
padding: 5px 10px;
|
||||
min-height: 100px;
|
||||
margin-bottom: 10px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item > span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-meta > span {
|
||||
color: #999;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-actions a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-actions a.delete {
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
.card-actions .edd-loading-ajax {
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-update-form {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-update-form label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-update-form input {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-update-form select {
|
||||
background: #fff;
|
||||
border: 1px solid #e4e4e4;
|
||||
height: 40px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-update-form select:first-of-type {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.edd-stripe-card-item .card-address-fields input,
|
||||
.edd-stripe-card-item .card-address-fields select {
|
||||
width: 49%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Checkout Fields */
|
||||
.edd-stripe-add-new-card label {
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
position: relative;
|
||||
line-height: 100%;
|
||||
font-size: 95%;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
.edd-stripe-add-new-card label:after {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
float: none;
|
||||
clear: both;
|
||||
height: 0;
|
||||
text-indent: -9999px;
|
||||
content: ".";
|
||||
}
|
||||
.edd-stripe-add-new-card span.edd-description {
|
||||
color: #666;
|
||||
font-size: 80%;
|
||||
display: block;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
.edd-stripe-add-new-card input.edd-input,
|
||||
.edd-stripe-add-new-card textarea.edd-input {
|
||||
display: inline-block;
|
||||
width: 70%;
|
||||
}
|
||||
.edd-stripe-add-new-card select.edd-select {
|
||||
display: block;
|
||||
width: 60%;
|
||||
}
|
||||
.edd-stripe-add-new-card select.edd-select.edd-select-small {
|
||||
display: inline;
|
||||
width: auto;
|
||||
}
|
||||
.edd-stripe-add-new-card input.edd-input.error,
|
||||
.edd-stripe-add-new-card textarea.edd-input.error {
|
||||
border-color: #c4554e;
|
||||
}
|
||||
.edd-stripe-add-new-card > p {
|
||||
margin: 0 0 21px;
|
||||
}
|
||||
.edd-stripe-add-new-card span.edd-required-indicator {
|
||||
color: #b94a48;
|
||||
display: inline;
|
||||
}
|
||||
.edd-stripe-add-new-card textarea,
|
||||
.edd-stripe-add-new-card input[type="text"],
|
||||
.edd-stripe-add-new-card input[type="email"],
|
||||
.edd-stripe-add-new-card input[type="password"],
|
||||
.edd-stripe-add-new-card input[type="tel"] {
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.edd-stripe-add-new-card input[type="radio"] {
|
||||
border: none;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.edd-stripe-add-new-card input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
.edd-stripe-add-new-card input[type="checkbox"] + label,
|
||||
.edd-stripe-add-new-card input[type="checkbox"] + label:after {
|
||||
display: inline;
|
||||
}
|
||||
.edd-stripe-add-new-card .edd-payment-icons {
|
||||
height: 32px;
|
||||
display: block;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.edd-stripe-add-new-card .edd-payment-icons img.payment-icon {
|
||||
max-height: 32px;
|
||||
width: auto;
|
||||
margin: 0 3px 0 0;
|
||||
float: left;
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd-payment-mode-wrap label {
|
||||
display: inline-block;
|
||||
margin: 0 20px 0 0;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd-payment-mode-wrap .edd-payment-mode-label {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset {
|
||||
border: 1px solid #eee;
|
||||
padding: 1.387em;
|
||||
margin: 0 0 21px;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd_purchase_submit,
|
||||
.edd-stripe-add-new-card #edd_discount_code,
|
||||
.edd-stripe-add-new-card #edd_register_account_fields {
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset fieldset {
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd-login-account-wrap,
|
||||
.edd-stripe-add-new-card #edd-new-account-wrap,
|
||||
.edd-stripe-add-new-card #edd_show_discount,
|
||||
.edd-stripe-add-new-card .edd-cart-adjustment,
|
||||
.edd-stripe-add-new-card #edd_final_total_wrap {
|
||||
background: #fafafa;
|
||||
color: #666;
|
||||
padding: 0.5em 1.387em;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd-discount-code-wrap,
|
||||
.edd-stripe-add-new-card #edd_final_total_wrap,
|
||||
.edd-stripe-add-new-card #edd_show_discount {
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.edd-stripe-add-new-card .edd-cart-adjustment {
|
||||
padding: 1.387em;
|
||||
}
|
||||
.edd-stripe-add-new-card .edd-cart-adjustment input.edd-input,
|
||||
.edd-stripe-add-new-card .edd-cart-adjustment input.edd-submit {
|
||||
display: inline-block;
|
||||
}
|
||||
.edd-stripe-add-new-card .edd-cart-adjustment input.edd-submit {
|
||||
padding: 3px 12px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd-discount-error-wrap {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
margin: 1em 0 0;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd-new-account-wrap,
|
||||
.edd-stripe-add-new-card #edd-login-account-wrap {
|
||||
margin: -1.387em -1.387em 21px;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
}
|
||||
.edd-stripe-add-new-card #edd_payment_mode_select {
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset#edd_register_fields #edd_checkout_user_info {
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset#edd_register_account_fields legend {
|
||||
padding-top: 11px;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset#edd_register_account_fields p.edd_register_password,
|
||||
.edd-stripe-add-new-card fieldset#edd_register_account_fields p.edd_login_password {
|
||||
margin: 0;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset#edd_cc_fields {
|
||||
border: 1px solid #f0f0f0;
|
||||
background: #f9f9f9;
|
||||
position: relative;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset#edd_cc_fields legend {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
.edd-stripe-add-new-card fieldset p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#edd_secure_site_wrapper {
|
||||
padding: 4px 4px 4px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.edd-stripe-add-new-card span.exp-divider {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stripe Elements - Card
|
||||
*/
|
||||
.edd-stripe-card-element.StripeElement,
|
||||
.edd-stripe-card-exp-element.StripeElement,
|
||||
.edd-stripe-card-cvc-element.StripeElement {
|
||||
box-sizing: border-box;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.edd-stripe-card-element.StripeElement--invalid {
|
||||
border-color: #c4554e !important;
|
||||
}
|
||||
|
||||
#edd-stripe-card-errors:not(:empty) {
|
||||
margin: 20px 0 0;
|
||||
}
|
||||
|
||||
#edd-card-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#edd-card-details-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#edd-card-details-wrap p:empty {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#edd-card-exp-wrap,
|
||||
#edd-card-cvv-wrap {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
#edd-stripe-card-element-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#edd_checkout_form_wrap .edd-stripe-new-card span.card-type {
|
||||
background-size: 32px 24px !important;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
top: 50%;
|
||||
transform: translate3d(0, -50%, 0);
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Buy Now" modal.
|
||||
*/
|
||||
.edds-buy-now-modal {
|
||||
width: 500px;
|
||||
|
||||
.edds-modal__close {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
#edd_checkout_form_wrap {
|
||||
|
||||
input.edd-input,
|
||||
textarea.edd-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#edd_purchase_submit {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.edds-field-spacer-shim {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.edd-alert-error {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
#edd-stripe-card-errors:not(:empty) {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.edd-alert-error {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
:root {
|
||||
--edds-modal-grid-unit: 1rem;
|
||||
--edds-modal-overlay: rgba(0, 0, 0, 0.60);
|
||||
}
|
||||
|
||||
.edds-modal__overlay {
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--edds-modal-overlay);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.edds-modal__container {
|
||||
background-color: #fff;
|
||||
min-width: 350px;
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.admin-bar .edds-modal__container {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.edds-modal__header {
|
||||
padding: calc(var(--edds-modal-grid-unit) * 1.5);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.edds-modal__title {
|
||||
text-align: left;
|
||||
font-size: 150%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.edds-modal__close {
|
||||
line-height: 1;
|
||||
padding: 1rem;
|
||||
|
||||
&:before {
|
||||
content: "\2715";
|
||||
}
|
||||
}
|
||||
|
||||
.edds-modal__content {
|
||||
margin: calc(var(--edds-modal-grid-unit) * 1.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animations
|
||||
*/
|
||||
@keyframes eddsSlideIn {
|
||||
from {
|
||||
transform: translateY(15%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes eddsSlideOut {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
transform: translateY(15%);
|
||||
}
|
||||
}
|
||||
|
||||
.edds-modal.has-slide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.edds-modal.has-slide.is-open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.edds-modal.has-slide[aria-hidden="false"] .edds-modal__container {
|
||||
animation: eddsSlideIn 0.3s cubic-bezier(0.0, 0.0, 0.2, 1);
|
||||
}
|
||||
|
||||
.edds-modal.has-slide[aria-hidden="true"] .edds-modal__container {
|
||||
animation: eddsSlideOut 0.3s cubic-bezier(0.0, 0.0, 0.2, 1);
|
||||
}
|
||||
|
||||
.edds-modal.has-slide .edds-modal__container,
|
||||
.edds-modal.has-slide .edds-modal__overlay {
|
||||
will-change: transform;
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
.edds-prb {
|
||||
margin: 15px 0;
|
||||
display: none;
|
||||
|
||||
&__or {
|
||||
font-size: 90%;
|
||||
text-align: center;
|
||||
margin: 15px 0;
|
||||
overflow: hidden;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
background-color: rgba(0, 0, 0, .10);
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 1px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
&::before {
|
||||
right: 0.5em;
|
||||
margin-left: -50%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
left: 0.5em;
|
||||
margin-right: -50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin loadingState {
|
||||
|
||||
&.loading {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
> * {
|
||||
opacity: 0.65;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purchase link loading state.
|
||||
*
|
||||
* Disables interaction while redirecting.
|
||||
*/
|
||||
.edd_download_purchase_form {
|
||||
|
||||
@include loadingState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkout
|
||||
*/
|
||||
#edd_checkout_form_wrap {
|
||||
|
||||
@include loadingState;
|
||||
|
||||
&:not(.edd-prb--is-active) #edd-payment-mode-wrap #edd-gateway-option-stripe-prb {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.edds-prb {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.edds-prb__or {
|
||||
display: none;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=102)}({102:function(e,n,t){(function(e){jQuery((function(){jQuery(".edds-admin-notice").each((function(){var n=e(this),t=n.data("id"),r=n.data("nonce");n.on("click",".notice-dismiss",(function(e){return e.preventDefault(),e.stopPropagation(),wp.ajax.post("edds_admin_notices_dismiss_ajax",{id:t,nonce:r})}))}))}))}).call(this,t(5))},5:function(e,n){e.exports=jQuery}});
|
@ -0,0 +1,104 @@
|
||||
/* global $, edd_stripe_admin */
|
||||
|
||||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
import './../../../css/src/admin.scss';
|
||||
import './settings/index.js';
|
||||
|
||||
let testModeCheckbox;
|
||||
let testModeToggleNotice;
|
||||
|
||||
$( document ).ready( function() {
|
||||
testModeCheckbox = document.getElementById( 'edd_settings[test_mode]' );
|
||||
if ( testModeCheckbox ) {
|
||||
testModeToggleNotice = document.getElementById( 'edd_settings[stripe_connect_test_mode_toggle_notice]' );
|
||||
EDD_Stripe_Connect_Scripts.init();
|
||||
}
|
||||
|
||||
// Toggle API keys.
|
||||
$( '.edds-api-key-toggle button' ).on( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
$( '.edds-api-key-toggle, .edds-api-key-row' )
|
||||
.toggleClass( 'edd-hidden' );
|
||||
} );
|
||||
} );
|
||||
|
||||
const EDD_Stripe_Connect_Scripts = {
|
||||
|
||||
init() {
|
||||
this.listeners();
|
||||
},
|
||||
|
||||
listeners() {
|
||||
const self = this;
|
||||
|
||||
testModeCheckbox.addEventListener( 'change', function() {
|
||||
// Don't run these events if Stripe is not enabled.
|
||||
if ( ! edd_stripe_admin.stripe_enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this.checked ) {
|
||||
if ( 'false' === edd_stripe_admin.test_key_exists ) {
|
||||
self.showNotice( testModeToggleNotice, 'warning' );
|
||||
self.addHiddenMarker();
|
||||
} else {
|
||||
self.hideNotice( testModeToggleNotice );
|
||||
const hiddenMarker = document.getElementById( 'edd-test-mode-toggled' );
|
||||
if ( hiddenMarker ) {
|
||||
hiddenMarker.parentNode.removeChild( hiddenMarker );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! this.checked ) {
|
||||
if ( 'false' === edd_stripe_admin.live_key_exists ) {
|
||||
self.showNotice( testModeToggleNotice, 'warning' );
|
||||
self.addHiddenMarker();
|
||||
} else {
|
||||
self.hideNotice( testModeToggleNotice );
|
||||
const hiddenMarker = document.getElementById( 'edd-test-mode-toggled' );
|
||||
if ( hiddenMarker ) {
|
||||
hiddenMarker.parentNode.removeChild( hiddenMarker );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
addHiddenMarker() {
|
||||
const submit = document.getElementById( 'submit' );
|
||||
|
||||
if ( ! submit ) {
|
||||
return;
|
||||
}
|
||||
|
||||
submit.parentNode.insertAdjacentHTML( 'beforeend', '<input type="hidden" class="edd-hidden" id="edd-test-mode-toggled" name="edd-test-mode-toggled" />' );
|
||||
},
|
||||
|
||||
showNotice( element = false, type = 'error' ) {
|
||||
if ( ! element ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( typeof element !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.className = 'notice notice-' + type;
|
||||
},
|
||||
|
||||
hideNotice( element = false ) {
|
||||
if ( ! element ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( typeof element !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.className = 'edd-hidden';
|
||||
},
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
/* global wp, jQuery */
|
||||
|
||||
/**
|
||||
* Handle dismissing admin notices.
|
||||
*/
|
||||
jQuery( () => {
|
||||
/**
|
||||
* Loops through each admin notice on the page for processing.
|
||||
*
|
||||
* @param {HTMLElement} noticeEl Notice element.
|
||||
*/
|
||||
jQuery( '.edds-admin-notice' ).each( function() {
|
||||
const notice = $( this );
|
||||
const id = notice.data( 'id' );
|
||||
const nonce = notice.data( 'nonce' );
|
||||
|
||||
/**
|
||||
* Listens for a click event on the dismiss button, and dismisses the notice.
|
||||
*
|
||||
* @param {Event} e Click event.
|
||||
* @return {jQuery.Deferred} Deferred object.
|
||||
*/
|
||||
notice.on( 'click', '.notice-dismiss', ( e ) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
return wp.ajax.post(
|
||||
'edds_admin_notices_dismiss_ajax',
|
||||
{
|
||||
id,
|
||||
nonce,
|
||||
}
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './requirements.js';
|
||||
import './stripe-connect.js';
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { domReady } from 'utils';
|
||||
|
||||
/**
|
||||
* Hides "Save Changes" button if showing the special settings placeholder.
|
||||
*/
|
||||
domReady( () => {
|
||||
const containerEl = document.querySelector( '.edds-requirements-not-met' );
|
||||
|
||||
if ( ! containerEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide "Save Changes" button.
|
||||
document.querySelector( '.edd-settings-wrap .submit' ).style.display = 'none';
|
||||
} );
|
||||
|
||||
/**
|
||||
* Moves "Payment Gateways" notice under Stripe.
|
||||
* Disables/unchecks the checkbox.
|
||||
*/
|
||||
domReady( () => {
|
||||
const noticeEl = document.getElementById( 'edds-payment-gateways-stripe-unmet-requirements' );
|
||||
|
||||
if ( ! noticeEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stripeLabel = document.querySelector( 'label[for="edd_settings[gateways][stripe]"]' );
|
||||
stripeLabel.parentNode.insertBefore( noticeEl, stripeLabel.nextSibling );
|
||||
|
||||
const stripeCheck = document.getElementById( 'edd_settings[gateways][stripe]' );
|
||||
stripeCheck.disabled = true;
|
||||
stripeCheck.checked = false;
|
||||
|
||||
noticeEl.insertBefore( stripeCheck, noticeEl.querySelector( 'p' ) );
|
||||
noticeEl.insertBefore( stripeLabel, noticeEl.querySelector( 'p' ) );
|
||||
} );
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { domReady, apiRequest } from 'utils';
|
||||
|
||||
// Wait for DOM.
|
||||
domReady( () => {
|
||||
const containerEl = document.getElementById( 'edds-stripe-connect-account' );
|
||||
const actionsEl = document.getElementById( 'edds-stripe-disconnect-reconnect' );
|
||||
|
||||
if ( ! containerEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
return apiRequest( 'edds_stripe_connect_account_info', {
|
||||
...containerEl.dataset,
|
||||
} )
|
||||
.done( ( response ) => {
|
||||
containerEl.innerHTML = response.message;
|
||||
containerEl.classList.add( `notice-${ response.status }` );
|
||||
if ( response.actions ) {
|
||||
actionsEl.innerHTML = response.actions;
|
||||
}
|
||||
} )
|
||||
.fail( ( error ) => {
|
||||
containerEl.innerHTML = error.message;
|
||||
containerEl.classList.add( 'notice-error' );
|
||||
} );
|
||||
} );
|
@ -0,0 +1,2 @@
|
||||
export { default as Modal } from './modal';
|
||||
export { paymentMethods } from './payment-methods';
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
|
||||
// Import Polyfills for MicroModal IE 11 support.
|
||||
// https://github.com/Ghosh/micromodal#ie-11-and-below
|
||||
// https://github.com/ghosh/Micromodal/issues/49#issuecomment-424213347
|
||||
// https://github.com/ghosh/Micromodal/issues/49#issuecomment-517916416
|
||||
import 'core-js/modules/es.object.assign';
|
||||
import 'core-js/modules/es.array.from';
|
||||
|
||||
import MicroModal from 'micromodal';
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
disableScroll: true,
|
||||
awaitOpenAnimation: true,
|
||||
awaitCloseAnimation: true,
|
||||
};
|
||||
|
||||
function setup( options ) {
|
||||
const config = {
|
||||
...DEFAULT_CONFIG,
|
||||
...options,
|
||||
};
|
||||
|
||||
MicroModal.init( config );
|
||||
}
|
||||
|
||||
function open( modalId, options ) {
|
||||
const config = {
|
||||
...DEFAULT_CONFIG,
|
||||
...options,
|
||||
};
|
||||
|
||||
MicroModal.show( modalId, config );
|
||||
}
|
||||
|
||||
function close( modalId ) {
|
||||
MicroModal.close( modalId );
|
||||
}
|
||||
|
||||
export default {
|
||||
setup,
|
||||
open,
|
||||
close,
|
||||
};
|
@ -0,0 +1,259 @@
|
||||
/* global $ */
|
||||
|
||||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
import { forEach, getNextSiblings } from 'utils'; // eslint-disable-line @wordpress/dependency-group
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function paymentMethods() {
|
||||
// Toggle only shows if using Full Address (for some reason).
|
||||
if ( getBillingFieldsToggle() ) {
|
||||
// Hide fields initially.
|
||||
toggleBillingFields( false );
|
||||
|
||||
/**
|
||||
* Binds change event to "Update billing address" toggle to show/hide address fields.
|
||||
*
|
||||
* @param {Event} e Change event.
|
||||
*/
|
||||
getBillingFieldsToggle().addEventListener( 'change', function( e ) {
|
||||
return toggleBillingFields( e.target.checked );
|
||||
} );
|
||||
}
|
||||
|
||||
// Payment method toggles.
|
||||
const existingPaymentMethods = document.querySelectorAll( '.edd-stripe-existing-card' );
|
||||
|
||||
if ( 0 !== existingPaymentMethods.length ) {
|
||||
forEach( existingPaymentMethods, function( existingPaymentMethod ) {
|
||||
/**
|
||||
* Binds change event to credit card toggles.
|
||||
*
|
||||
* @param {Event} e Change event.
|
||||
*/
|
||||
return existingPaymentMethod.addEventListener( 'change', function( e ) {
|
||||
return onPaymentSourceChange( e.target );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Simulate change of payment method to populate current fields.
|
||||
let currentPaymentMethod = document.querySelector( '.edd-stripe-existing-card:checked' );
|
||||
|
||||
if ( ! currentPaymentMethod ) {
|
||||
currentPaymentMethod = document.querySelector( '.edd-stripe-existing-card:first-of-type' );
|
||||
currentPaymentMethod.checked = true;
|
||||
}
|
||||
|
||||
const paymentMethodChangeEvent = document.createEvent( 'Event' );
|
||||
paymentMethodChangeEvent.initEvent( 'change', true, false );
|
||||
currentPaymentMethod.dispatchEvent( paymentMethodChangeEvent );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the billing fields can be toggled.
|
||||
*
|
||||
* @return {Bool} True if the toggle exists.
|
||||
*/
|
||||
function getBillingFieldsToggle() {
|
||||
return document.getElementById( 'edd-stripe-update-billing-address' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles billing fields visiblity.
|
||||
*
|
||||
* Assumes the toggle control is the first item in the "Billing Details" fieldset.
|
||||
*
|
||||
* @param {Bool} isVisible Billing item visibility.
|
||||
*/
|
||||
function toggleBillingFields( isVisible ) {
|
||||
const updateAddressWrapperEl = document.querySelector( '.edd-stripe-update-billing-address-wrapper' );
|
||||
|
||||
if ( ! updateAddressWrapperEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all elements after the toggle.
|
||||
const billingFieldWrappers = getNextSiblings( updateAddressWrapperEl );
|
||||
const billingAddressPreview = document.querySelector( '.edd-stripe-update-billing-address-current' );
|
||||
|
||||
billingFieldWrappers.forEach( function( wrap ) {
|
||||
wrap.style.display = isVisible ? 'block' : 'none';
|
||||
} );
|
||||
|
||||
// Hide address preview.
|
||||
if ( billingAddressPreview ) {
|
||||
billingAddressPreview.style.display = isVisible ? 'none' : 'block';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages UI state when the payment source changes.
|
||||
*
|
||||
* @param {HTMLElement} paymentSource Selected payment source. (Radio element with data).
|
||||
*/
|
||||
function onPaymentSourceChange( paymentSource ) {
|
||||
const isNew = 'new' === paymentSource.value;
|
||||
const newCardForm = document.querySelector( '.edd-stripe-new-card' );
|
||||
const billingAddressToggle = document.querySelector( '.edd-stripe-update-billing-address-wrapper' );
|
||||
|
||||
// Toggle card details field.
|
||||
newCardForm.style.display = isNew ? 'block' : 'none';
|
||||
|
||||
if ( billingAddressToggle ) {
|
||||
billingAddressToggle.style.display = isNew ? 'none' : 'block';
|
||||
}
|
||||
|
||||
// @todo don't be lazy.
|
||||
$( '.edd-stripe-card-radio-item' ).removeClass( 'selected' );
|
||||
$( paymentSource ).closest( '.edd-stripe-card-radio-item' ).addClass( 'selected' );
|
||||
|
||||
const addressFieldMap = {
|
||||
card_address: 'address_line1',
|
||||
card_address_2: 'address_line2',
|
||||
card_city: 'address_city',
|
||||
card_state: 'address_state',
|
||||
card_zip: 'address_zip',
|
||||
billing_country: 'address_country',
|
||||
};
|
||||
|
||||
// New card is being used, show fields and reset them.
|
||||
if ( isNew ) {
|
||||
// Reset all fields.
|
||||
for ( const addressEl in addressFieldMap ) {
|
||||
if ( ! addressFieldMap.hasOwnProperty( addressEl ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const addressField = document.getElementById( addressEl );
|
||||
|
||||
if ( addressField ) {
|
||||
addressField.value = '';
|
||||
addressField.selected = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Recalculate taxes.
|
||||
if ( window.EDD_Checkout.recalculate_taxes ) {
|
||||
window.EDD_Checkout.recalculate_taxes();
|
||||
}
|
||||
|
||||
// Show billing fields.
|
||||
toggleBillingFields( true );
|
||||
|
||||
// Existing card is being used.
|
||||
// Ensure the billing fields are hidden, and update their values with saved information.
|
||||
} else {
|
||||
const addressString = [];
|
||||
const billingDetailsEl = document.getElementById( paymentSource.id + '-billing-details' );
|
||||
|
||||
if ( ! billingDetailsEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide billing fields.
|
||||
toggleBillingFields( false );
|
||||
|
||||
// Uncheck "Update billing address"
|
||||
if ( getBillingFieldsToggle() ) {
|
||||
getBillingFieldsToggle().checked = false;
|
||||
}
|
||||
|
||||
// Update billing address fields with saved card values.
|
||||
const billingDetails = billingDetailsEl.dataset;
|
||||
|
||||
for ( const addressEl in addressFieldMap ) {
|
||||
if ( ! addressFieldMap.hasOwnProperty( addressEl ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const addressField = document.getElementById( addressEl );
|
||||
|
||||
if ( ! addressField ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = billingDetails[ addressFieldMap[ addressEl ] ];
|
||||
|
||||
// Set field value.
|
||||
addressField.value = value;
|
||||
|
||||
// Generate an address string from values.
|
||||
if ( '' !== value ) {
|
||||
addressString.push( value );
|
||||
}
|
||||
|
||||
// This field is required but does not have a saved value, show all fields.
|
||||
if ( addressField.required && '' === value ) {
|
||||
// @todo DRY up some of this DOM usage.
|
||||
toggleBillingFields( true );
|
||||
|
||||
if ( getBillingFieldsToggle() ) {
|
||||
getBillingFieldsToggle().checked = true;
|
||||
}
|
||||
|
||||
if ( billingAddressToggle ) {
|
||||
billingAddressToggle.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger change event when the Country field is updated.
|
||||
if ( 'billing_country' === addressEl ) {
|
||||
const changeEvent = document.createEvent( 'Event' );
|
||||
changeEvent.initEvent( 'change', true, true );
|
||||
addressField.dispatchEvent( changeEvent );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor AJAX requests for address changes.
|
||||
*
|
||||
* Wait for the "State" field to be updated based on the "Country" field's
|
||||
* change event. Once there is an AJAX response fill the "State" field with the
|
||||
* saved card's State data and recalculate taxes.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @param {Object} event
|
||||
* @param {Object} xhr
|
||||
* @param {Object} options
|
||||
*/
|
||||
$( document ).ajaxSuccess( function( event, xhr, options ) {
|
||||
if ( ! options || ! options.data || ! xhr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
options.data.includes( 'action=edd_get_shop_states' ) &&
|
||||
options.data.includes( 'field_name=card_state' ) &&
|
||||
( xhr.responseText && xhr.responseText.includes( 'card_state' ) )
|
||||
) {
|
||||
const stateField = document.getElementById( 'card_state' );
|
||||
|
||||
if ( stateField ) {
|
||||
stateField.value = billingDetails.address_state;
|
||||
|
||||
// Recalculate taxes.
|
||||
if ( window.EDD_Checkout.recalculate_taxes ) {
|
||||
window.EDD_Checkout.recalculate_taxes( stateField.value );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
// Update address string summary.
|
||||
const billingAddressPreview = document.querySelector( '.edd-stripe-update-billing-address-current' );
|
||||
|
||||
if ( billingAddressPreview ) {
|
||||
billingAddressPreview.innerText = addressString.join( ', ' );
|
||||
|
||||
const { brand, last4 } = billingDetails;
|
||||
|
||||
document.querySelector( '.edd-stripe-update-billing-address-brand' ).innerHTML = brand;
|
||||
document.querySelector( '.edd-stripe-update-billing-address-last4' ).innerHTML = last4;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/* global Stripe, edd_stripe_vars */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './../../../css/src/frontend.scss';
|
||||
import { domReady, apiRequest, generateNotice } from 'utils';
|
||||
|
||||
import {
|
||||
setupCheckout,
|
||||
setupProfile,
|
||||
setupPaymentHistory,
|
||||
setupBuyNow,
|
||||
setupDownloadPRB,
|
||||
setupCheckoutPRB,
|
||||
} from 'frontend/payment-forms';
|
||||
|
||||
import {
|
||||
paymentMethods,
|
||||
} from 'frontend/components/payment-methods';
|
||||
|
||||
import {
|
||||
mountCardElement,
|
||||
createPaymentForm as createElementsPaymentForm,
|
||||
getBillingDetails,
|
||||
getPaymentMethod,
|
||||
confirm as confirmIntent,
|
||||
handle as handleIntent,
|
||||
retrieve as retrieveIntent,
|
||||
} from 'frontend/stripe-elements';
|
||||
// eslint-enable @wordpress/dependency-group
|
||||
|
||||
( () => {
|
||||
try {
|
||||
window.eddStripe = new Stripe( edd_stripe_vars.publishable_key );
|
||||
|
||||
// Alias some functionality for external plugins.
|
||||
window.eddStripe._plugin = {
|
||||
domReady,
|
||||
apiRequest,
|
||||
generateNotice,
|
||||
mountCardElement,
|
||||
createElementsPaymentForm,
|
||||
getBillingDetails,
|
||||
getPaymentMethod,
|
||||
confirmIntent,
|
||||
handleIntent,
|
||||
retrieveIntent,
|
||||
paymentMethods,
|
||||
};
|
||||
|
||||
// Setup frontend components when DOM is ready.
|
||||
domReady(
|
||||
setupCheckout,
|
||||
setupProfile,
|
||||
setupPaymentHistory,
|
||||
setupBuyNow,
|
||||
setupDownloadPRB,
|
||||
setupCheckoutPRB,
|
||||
);
|
||||
} catch ( error ) {
|
||||
alert( error.message );
|
||||
}
|
||||
} )();
|
@ -0,0 +1,205 @@
|
||||
/* global jQuery, edd_scripts, edd_stripe_vars */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { forEach, domReady, apiRequest } from 'utils';
|
||||
import { Modal, paymentMethods } from 'frontend/components';
|
||||
import { paymentForm } from 'frontend/payment-forms/checkout'
|
||||
|
||||
/**
|
||||
* Adds a Download to the Cart.
|
||||
*
|
||||
* @param {number} downloadId Download ID.
|
||||
* @param {number} priceId Download Price ID.
|
||||
* @param {number} quantity Download quantity.
|
||||
* @param {string} nonce Nonce token.
|
||||
* @param {HTMLElement} addToCartForm Add to cart form.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
function addToCart( downloadId, priceId, quantity, nonce, addToCartForm ) {
|
||||
const data = {
|
||||
download_id: downloadId,
|
||||
price_id: priceId,
|
||||
quantity: quantity,
|
||||
nonce,
|
||||
post_data: jQuery( addToCartForm ).serialize(),
|
||||
};
|
||||
|
||||
return apiRequest( 'edds_add_to_cart', data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the Cart.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
function emptyCart() {
|
||||
return apiRequest( 'edds_empty_cart' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the Buy Now modal.
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {number} args.downloadId Download ID.
|
||||
* @param {number} args.priceId Download Price ID.
|
||||
* @param {number} args.quantity Download quantity.
|
||||
* @param {string} args.nonce Nonce token.
|
||||
* @param {HTMLElement} args.addToCartForm Add to cart form.
|
||||
*/
|
||||
function buyNowModal( args ) {
|
||||
const modalContent = document.querySelector( '#edds-buy-now-modal-content' );
|
||||
const modalLoading = '<span class="edd-loading-ajax edd-loading"></span>';
|
||||
|
||||
// Show modal.
|
||||
Modal.open( 'edds-buy-now', {
|
||||
/**
|
||||
* Adds the item to the Cart when opening.
|
||||
*/
|
||||
onShow() {
|
||||
modalContent.innerHTML = modalLoading;
|
||||
|
||||
const {
|
||||
downloadId,
|
||||
priceId,
|
||||
quantity,
|
||||
nonce,
|
||||
addToCartForm,
|
||||
} = args;
|
||||
|
||||
addToCart(
|
||||
downloadId,
|
||||
priceId,
|
||||
quantity,
|
||||
nonce,
|
||||
addToCartForm
|
||||
)
|
||||
.then( ( { checkout } ) => {
|
||||
// Show Checkout HTML.
|
||||
modalContent.innerHTML = checkout;
|
||||
|
||||
// Reinitialize core JS.
|
||||
window.EDD_Checkout.init();
|
||||
|
||||
const totalEl = document.querySelector( '#edds-buy-now-modal-content .edd_cart_amount' );
|
||||
const total = parseFloat( totalEl.dataset.total );
|
||||
|
||||
// Reinitialize Stripe JS if a payment is required.
|
||||
if ( total > 0 ) {
|
||||
paymentForm();
|
||||
paymentMethods();
|
||||
}
|
||||
} )
|
||||
.fail( ( { message } ) => {
|
||||
// Show error message.
|
||||
document.querySelector( '#edds-buy-now-modal-content' ).innerHTML = message;
|
||||
} );
|
||||
},
|
||||
/**
|
||||
* Empties Cart on close.
|
||||
*/
|
||||
onClose() {
|
||||
emptyCart();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
// DOM ready.
|
||||
export function setup() {
|
||||
|
||||
// Find all "Buy Now" links on the page.
|
||||
forEach( document.querySelectorAll( '.edds-buy-now' ), ( el ) => {
|
||||
|
||||
// Don't use modal if "Free Downloads" is active and available for this download.
|
||||
// https://easydigitaldownloads.com/downloads/free-downloads/
|
||||
if ( el.classList.contains( 'edd-free-download' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches "Buy Now" modal when clicking "Buy Now" link.
|
||||
*
|
||||
* @param {Object} e Click event.
|
||||
*/
|
||||
el.addEventListener( 'click', ( e ) => {
|
||||
const { downloadId, nonce } = e.currentTarget.dataset;
|
||||
|
||||
// Stop other actions if a Download ID is found.
|
||||
if ( ! downloadId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
// Gather Download information.
|
||||
let priceId = 0;
|
||||
let quantity = 1;
|
||||
|
||||
const addToCartForm = e.currentTarget.closest(
|
||||
'.edd_download_purchase_form'
|
||||
);
|
||||
|
||||
// Price ID.
|
||||
const priceIdEl = addToCartForm.querySelector(
|
||||
`.edd_price_option_${downloadId}:checked`
|
||||
);
|
||||
|
||||
if ( priceIdEl ) {
|
||||
priceId = priceIdEl.value;
|
||||
}
|
||||
|
||||
// Quantity.
|
||||
const quantityEl = addToCartForm.querySelector(
|
||||
'input[name="edd_download_quantity"]'
|
||||
);
|
||||
|
||||
if ( quantityEl ) {
|
||||
quantity = quantityEl.value;
|
||||
}
|
||||
|
||||
buyNowModal( {
|
||||
downloadId,
|
||||
priceId,
|
||||
quantity,
|
||||
nonce,
|
||||
addToCartForm
|
||||
} );
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
/**
|
||||
* Replaces submit button text after validation errors.
|
||||
*
|
||||
* If there are no other items in the cart the core javascript will replace
|
||||
* the button text with the value for a $0 cart (usually "Free Download")
|
||||
* because the script variables were constructed when nothing was in the cart.
|
||||
*/
|
||||
jQuery( document.body ).on( 'edd_checkout_error', () => {
|
||||
const submitButtonEl = document.querySelector(
|
||||
'#edds-buy-now #edd-purchase-button'
|
||||
);
|
||||
|
||||
if ( ! submitButtonEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { i18n: { completePurchase } } = edd_stripe_vars;
|
||||
|
||||
const amountEl = document.querySelector( '.edd_cart_amount' );
|
||||
const { total, totalCurrency } = amountEl.dataset;
|
||||
|
||||
if ( '0' === total ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For some reason a delay is needed to override the value set by
|
||||
// https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/assets/js/edd-ajax.js#L414
|
||||
setTimeout( () => {
|
||||
submitButtonEl.value = `${ totalCurrency } - ${ completePurchase }`;
|
||||
}, 10 );
|
||||
} );
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/* global $, edd_scripts */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
// eslint-disable @wordpress/dependency-group
|
||||
import { paymentMethods } from 'frontend/components';
|
||||
// eslint-enable @wordpress/dependency-group
|
||||
|
||||
import { paymentForm } from './payment-form.js';
|
||||
|
||||
export * from './payment-form.js';
|
||||
|
||||
export function setup() {
|
||||
if ( '1' !== edd_scripts.is_checkout ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initial load for single gateway.
|
||||
const singleGateway = document.querySelector( 'input[name="edd-gateway"]' );
|
||||
|
||||
if ( singleGateway && 'stripe' === singleGateway.value ) {
|
||||
paymentForm();
|
||||
paymentMethods();
|
||||
}
|
||||
|
||||
// Gateway switch.
|
||||
$( document.body ).on( 'edd_gateway_loaded', ( e, gateway ) => {
|
||||
if ( 'stripe' !== gateway ) {
|
||||
return;
|
||||
}
|
||||
|
||||
paymentForm();
|
||||
paymentMethods();
|
||||
} );
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
/* global $, edd_stripe_vars, edd_global_vars */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
createPaymentForm as createElementsPaymentForm,
|
||||
getPaymentMethod,
|
||||
capture as captureIntent,
|
||||
handle as handleIntent,
|
||||
} from 'frontend/stripe-elements'; // eslint-disable-line @wordpress/dependency-group
|
||||
|
||||
import { apiRequest, generateNotice } from 'utils'; // eslint-disable-line @wordpress/dependency-group
|
||||
|
||||
/**
|
||||
* Binds Payment submission functionality.
|
||||
*
|
||||
* Resets before rebinding to avoid duplicate events
|
||||
* during gateway switching.
|
||||
*/
|
||||
export function paymentForm() {
|
||||
// Mount Elements.
|
||||
createElementsPaymentForm( window.eddStripe.elements() );
|
||||
|
||||
// Bind form submission.
|
||||
// Needs to be jQuery since that is what core submits against.
|
||||
$( '#edd_purchase_form' ).off( 'submit', onSubmit );
|
||||
$( '#edd_purchase_form' ).on( 'submit', onSubmit );
|
||||
|
||||
// SUPER ghetto way to watch for core form validation because no events are in place.
|
||||
// Called after the purchase form is submitted (via `click` or `submit`)
|
||||
$( document ).off( 'ajaxSuccess', watchInitialValidation );
|
||||
$( document ).on( 'ajaxSuccess', watchInitialValidation );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes Stripe gateway-specific functionality after core AJAX validation has run.
|
||||
*/
|
||||
async function onSubmitDelay() {
|
||||
try {
|
||||
// Form data to send to intent requests.
|
||||
let formData = $( '#edd_purchase_form' ).serialize(),
|
||||
tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
// Retrieve or create a PaymentMethod.
|
||||
const paymentMethod = await getPaymentMethod( document.getElementById( 'edd_purchase_form' ), window.eddStripe.cardElement );
|
||||
|
||||
// Run the modified `_edds_process_purchase_form` and create an Intent.
|
||||
const {
|
||||
intent: initialIntent,
|
||||
nonce: refreshedNonce
|
||||
} = await processForm( paymentMethod.id, paymentMethod.exists );
|
||||
|
||||
// Update existing nonce value in DOM form data in case data is retrieved
|
||||
// again directly from the DOM.
|
||||
$( '#edd-process-checkout-nonce' ).val( refreshedNonce );
|
||||
|
||||
// Handle any actions required by the Intent State Machine (3D Secure, etc).
|
||||
const handledIntent = await handleIntent(
|
||||
initialIntent,
|
||||
{
|
||||
form_data: formData += `&edd-process-checkout-nonce=${ refreshedNonce }`,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
}
|
||||
);
|
||||
|
||||
// Create an EDD payment record.
|
||||
const { intent, nonce } = await createPayment( handledIntent );
|
||||
|
||||
// Capture any unpcaptured intents.
|
||||
const finalIntent = await captureIntent(
|
||||
intent,
|
||||
{},
|
||||
nonce
|
||||
);
|
||||
|
||||
// Attempt to transition payment status and redirect.
|
||||
// @todo Maybe confirm payment status as well? Would need to generate a custom
|
||||
// response because the private EDD_Payment properties are not available.
|
||||
if (
|
||||
( 'succeeded' === finalIntent.status ) ||
|
||||
( 'canceled' === finalIntent.status && 'abandoned' === finalIntent.cancellation_reason )
|
||||
) {
|
||||
await completePayment( finalIntent, nonce );
|
||||
|
||||
window.location.replace( edd_stripe_vars.successPageUri );
|
||||
} else {
|
||||
window.location.replace( edd_stripe_vars.failurePageUri );
|
||||
}
|
||||
} catch ( error ) {
|
||||
handleException( error );
|
||||
enableForm();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the purchase form.
|
||||
*
|
||||
* Generates purchase data for the current session and
|
||||
* uses the PaymentMethod to generate an Intent based on data.
|
||||
*
|
||||
* @param {string} paymentMethodId PaymentMethod ID.
|
||||
* @param {Bool} paymentMethodExists If the PaymentMethod has already been attached to a customer.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function processForm( paymentMethodId, paymentMethodExists ) {
|
||||
let tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
return apiRequest( 'edds_process_purchase_form', {
|
||||
// Send available form data.
|
||||
form_data: $( '#edd_purchase_form' ).serialize(),
|
||||
payment_method_id: paymentMethodId,
|
||||
payment_method_exists: paymentMethodExists,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a Payment in EDD.
|
||||
*
|
||||
* @param {object} intent Intent.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function createPayment( intent ) {
|
||||
const paymentForm = $( '#edd_purchase_form' ),
|
||||
tokenInput = $( '#edd-process-stripe-token' );
|
||||
let formData = paymentForm.serialize();
|
||||
|
||||
// Attempt to find the Checkout nonce directly.
|
||||
if ( paymentForm.length === 0 ) {
|
||||
const nonce = $( '#edd-process-checkout-nonce' ).val();
|
||||
formData = `edd-process-checkout-nonce=${ nonce }`
|
||||
}
|
||||
|
||||
return apiRequest( 'edds_create_payment', {
|
||||
form_data: formData,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
intent,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a Payment in EDD.
|
||||
*
|
||||
* @param {object} intent Intent.
|
||||
* @param {string} refreshedNonce A refreshed nonce that might be needed if the
|
||||
* user logged in.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function completePayment( intent, refreshedNonce ) {
|
||||
const paymentForm = $( '#edd_purchase_form' );
|
||||
let formData = paymentForm.serialize(),
|
||||
tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
// Attempt to find the Checkout nonce directly.
|
||||
if ( paymentForm.length === 0 ) {
|
||||
const nonce = $( '#edd-process-checkout-nonce' ).val();
|
||||
formData = `edd-process-checkout-nonce=${ nonce }`;
|
||||
}
|
||||
|
||||
// Add the refreshed nonce if available.
|
||||
if ( refreshedNonce ) {
|
||||
formData += `&edd-process-checkout-nonce=${ refreshedNonce }`;
|
||||
}
|
||||
|
||||
return apiRequest( 'edds_complete_payment', {
|
||||
form_data: formData,
|
||||
intent,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listen for initial EDD core validation.
|
||||
*
|
||||
* @param {Object} event Event.
|
||||
* @param {Object} xhr AJAX request.
|
||||
* @param {Object} options Request options.
|
||||
*/
|
||||
function watchInitialValidation( event, xhr, options ) {
|
||||
if ( ! options || ! options.data || ! xhr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
options.data.includes( 'action=edd_process_checkout' ) &&
|
||||
options.data.includes( 'edd-gateway=stripe' ) &&
|
||||
( xhr.responseText && 'success' === xhr.responseText.trim() )
|
||||
) {
|
||||
return onSubmitDelay();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* EDD core listens to a a `click` event on the Checkout form submit button.
|
||||
*
|
||||
* This submit event handler captures true submissions and triggers a `click`
|
||||
* event so EDD core can take over as normoal.
|
||||
*
|
||||
* @param {Object} event submit Event.
|
||||
*/
|
||||
function onSubmit( event ) {
|
||||
// Ensure we are dealing with the Stripe gateway.
|
||||
if ( ! (
|
||||
// Stripe is selected gateway and total is larger than 0.
|
||||
$( 'input[name="edd-gateway"]' ).val() === 'stripe' &&
|
||||
$( '.edd_cart_total .edd_cart_amount' ).data( 'total' ) > 0
|
||||
) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// While this function is tied to the submit event, block submission.
|
||||
event.preventDefault();
|
||||
|
||||
// Simulate a mouse click on the Submit button.
|
||||
//
|
||||
// If the form is submitted via the "Enter" key we need to ensure the core
|
||||
// validation is run.
|
||||
//
|
||||
// When that is run and then the form is resubmitted
|
||||
// the click event won't do anything because the button will be disabled.
|
||||
$( '#edd_purchase_form #edd_purchase_submit [type=submit]' ).trigger( 'click' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the Checkout form for further submissions.
|
||||
*/
|
||||
function enableForm() {
|
||||
// Update button text.
|
||||
document.querySelector( '#edd_purchase_form #edd_purchase_submit [type=submit]' ).value = edd_global_vars.complete_purchase;
|
||||
|
||||
// Enable form.
|
||||
$( '.edd-loading-ajax' ).remove();
|
||||
$( '.edd_errors' ).remove();
|
||||
$( '.edd-error' ).hide();
|
||||
$( '#edd-purchase-button' ).attr( 'disabled', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles error output for stripe.js promises, or jQuery AJAX promises.
|
||||
*
|
||||
* @link https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/assets/js/edd-ajax.js#L390
|
||||
*
|
||||
* @param {Object} error Error data.
|
||||
*/
|
||||
function handleException( error ) {
|
||||
let { code, message } = error;
|
||||
const { elementsOptions: { i18n: { errorMessages } } } = window.edd_stripe_vars;
|
||||
|
||||
if ( ! message ) {
|
||||
message = edd_stripe_vars.generic_error;
|
||||
}
|
||||
|
||||
const localizedMessage = code && errorMessages[code] ? errorMessages[code] : message;
|
||||
|
||||
const notice = generateNotice( localizedMessage );
|
||||
|
||||
// Hide previous messages.
|
||||
// @todo These should all be in a container, but that's not how core works.
|
||||
$( '.edd-stripe-alert' ).remove();
|
||||
$( edd_global_vars.checkout_error_anchor ).before( notice );
|
||||
$( document.body ).trigger( 'edd_checkout_error', [ error ] );
|
||||
|
||||
if ( window.console && error.responseText ) {
|
||||
window.console.error( error.responseText );
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
export { setup as setupCheckout, createPayment, completePayment } from './checkout';
|
||||
export { setup as setupProfile } from './profile-editor';
|
||||
export { setup as setupPaymentHistory } from './payment-receipt';
|
||||
export { setup as setupBuyNow } from './buy-now';
|
||||
export {
|
||||
setupDownload as setupDownloadPRB,
|
||||
setupCheckout as setupCheckoutPRB,
|
||||
} from './payment-request';
|
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
// eslint-disable @wordpress/dependency-group
|
||||
import { paymentMethods } from 'frontend/components/payment-methods';
|
||||
// eslint-enable @wordpress/dependency-group
|
||||
|
||||
import { paymentForm } from './payment-form.js';
|
||||
|
||||
export function setup() {
|
||||
if ( ! document.getElementById( 'edds-update-payment-method' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
paymentForm();
|
||||
paymentMethods();
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/* global edd_stripe_vars */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
// eslint-disable @wordpress/dependency-group
|
||||
import {
|
||||
getPaymentMethod,
|
||||
createPaymentForm as createElementsPaymentForm,
|
||||
handle as handleIntent,
|
||||
retrieve as retrieveIntent,
|
||||
} from 'frontend/stripe-elements';
|
||||
|
||||
import { generateNotice, apiRequest } from 'utils';
|
||||
// eslint-enable @wordpress/dependency-group
|
||||
|
||||
/**
|
||||
* Binds events and sets up "Update Payment Method" form.
|
||||
*/
|
||||
export function paymentForm() {
|
||||
// Mount Elements.
|
||||
createElementsPaymentForm( window.eddStripe.elements() );
|
||||
|
||||
document.getElementById( 'edds-update-payment-method' ).addEventListener( 'submit', onAuthorizePayment );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup PaymentMethods.
|
||||
*
|
||||
* Moves the active item to the currently authenticating PaymentMethod.
|
||||
*/
|
||||
function setPaymentMethod() {
|
||||
const form = document.getElementById( 'edds-update-payment-method' );
|
||||
const input = document.getElementById( form.dataset.paymentMethod );
|
||||
|
||||
// Select the correct PaymentMethod after load.
|
||||
if ( input ) {
|
||||
const changeEvent = document.createEvent( 'Event' );
|
||||
|
||||
changeEvent.initEvent( 'change', true, true );
|
||||
input.checked = true;
|
||||
input.dispatchEvent( changeEvent );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize a PaymentIntent.
|
||||
*
|
||||
* @param {Event} e submtit event.
|
||||
*/
|
||||
async function onAuthorizePayment( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = document.getElementById( 'edds-update-payment-method' );
|
||||
|
||||
disableForm();
|
||||
|
||||
try {
|
||||
const paymentMethod = await getPaymentMethod( form, window.eddStripe.cardElement );
|
||||
|
||||
// Handle PaymentIntent.
|
||||
const intent = await retrieveIntent( form.dataset.paymentIntent, 'payment_method' );
|
||||
|
||||
const handledIntent = await handleIntent( intent, {
|
||||
payment_method: paymentMethod.id,
|
||||
} );
|
||||
|
||||
// Attempt to transition payment status and redirect.
|
||||
const authorization = await completeAuthorization( handledIntent.id );
|
||||
|
||||
if ( authorization.payment ) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
throw authorization;
|
||||
}
|
||||
} catch ( error ) {
|
||||
handleException( error );
|
||||
enableForm();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a Payment after the Intent has been authorized.
|
||||
*
|
||||
* @param {string} intentId Intent ID.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function completeAuthorization( intentId ) {
|
||||
return apiRequest( 'edds_complete_payment_authorization', {
|
||||
intent_id: intentId,
|
||||
'edds-complete-payment-authorization': document.getElementById(
|
||||
'edds-complete-payment-authorization'
|
||||
).value
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables "Add New" form.
|
||||
*/
|
||||
function disableForm() {
|
||||
const submit = document.getElementById( 'edds-update-payment-method-submit' );
|
||||
|
||||
submit.value = submit.dataset.loading;
|
||||
submit.disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables "Add New" form.
|
||||
*/
|
||||
function enableForm() {
|
||||
const submit = document.getElementById( 'edds-update-payment-method-submit' );
|
||||
|
||||
submit.value = submit.dataset.submit;
|
||||
submit.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a notice (success or error) for authorizing a card.
|
||||
*
|
||||
* @param {Object} error Error with message to output.
|
||||
*/
|
||||
export function handleException( error ) {
|
||||
// Create the new notice.
|
||||
const notice = generateNotice(
|
||||
( error && error.message ) ? error.message : edd_stripe_vars.generic_error,
|
||||
'error'
|
||||
);
|
||||
|
||||
const container = document.getElementById( 'edds-update-payment-method-errors' );
|
||||
|
||||
container.innerHTML = '';
|
||||
container.appendChild( notice );
|
||||
}
|
@ -0,0 +1,458 @@
|
||||
/* global edd_scripts, jQuery */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { parseDataset } from './';
|
||||
import { apiRequest, forEach, outputNotice, clearNotice } from 'utils';
|
||||
import { handle as handleIntent } from 'frontend/stripe-elements';
|
||||
import { createPayment, completePayment } from 'frontend/payment-forms';
|
||||
|
||||
let IS_PRB_GATEWAY;
|
||||
|
||||
/**
|
||||
* Disables the "Express Checkout" payment gateway.
|
||||
* Switches to the next in the list.
|
||||
*/
|
||||
function hideAndSwitchGateways() {
|
||||
IS_PRB_GATEWAY = false;
|
||||
|
||||
const gatewayRadioEl = document.getElementById( 'edd-gateway-option-stripe-prb' );
|
||||
|
||||
if ( ! gatewayRadioEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove radio option.
|
||||
gatewayRadioEl.remove();
|
||||
|
||||
// Recount available gateways and hide selector if needed.
|
||||
const gateways = document.querySelectorAll( '.edd-gateway-option' );
|
||||
const nextGateway = gateways[0];
|
||||
const nextGatewayInput = nextGateway.querySelector( 'input' );
|
||||
|
||||
// Toggle radio.
|
||||
nextGatewayInput.checked = true;
|
||||
nextGateway.classList.add( 'edd-gateway-option-selected' );
|
||||
|
||||
// Load gateway.
|
||||
edd_load_gateway( nextGatewayInput.value );
|
||||
|
||||
// Hide wrapper.
|
||||
if ( 1 === gateways.length ) {
|
||||
document.getElementById( 'edd_payment_mode_select_wrap' ).remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the click event on the Payment Request Button.
|
||||
*
|
||||
* @param {Event} event Click event.
|
||||
*/
|
||||
function onClick( event ) {
|
||||
const errorContainer = document.getElementById( 'edds-prb-error-wrap' );
|
||||
const {
|
||||
checkout_agree_to_terms,
|
||||
checkout_agree_to_privacy,
|
||||
} = edd_stripe_vars;
|
||||
|
||||
const termsEl = document.getElementById( 'edd_agree_to_terms' );
|
||||
|
||||
if ( termsEl ) {
|
||||
if ( false === termsEl.checked ) {
|
||||
event.preventDefault();
|
||||
|
||||
outputNotice( {
|
||||
errorMessage: checkout_agree_to_terms,
|
||||
errorContainer,
|
||||
} );
|
||||
} else {
|
||||
clearNotice( errorContainer );
|
||||
}
|
||||
}
|
||||
|
||||
const privacyEl = document.getElementById( 'edd-agree-to-privacy-policy' );
|
||||
|
||||
if ( privacyEl && false === privacyEl.checked ) {
|
||||
if ( false === privacyEl.checked ) {
|
||||
event.preventDefault();
|
||||
|
||||
outputNotice( {
|
||||
errorMessage: checkout_agree_to_privacy,
|
||||
errorContainer,
|
||||
} );
|
||||
} else {
|
||||
clearNotice( errorContainer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes to the purchase link form by updating the Payment Request object.
|
||||
*
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} checkoutForm Checkout form.
|
||||
*/
|
||||
async function onChange( paymentRequest, checkoutForm ) {
|
||||
try {
|
||||
// Calculate and gather price information.
|
||||
const {
|
||||
'display-items': displayItems,
|
||||
...paymentRequestData
|
||||
} = await apiRequest( 'edds_prb_ajax_get_options' );
|
||||
|
||||
// Update the Payment Request with server-side data.
|
||||
paymentRequest.update( {
|
||||
displayItems,
|
||||
...paymentRequestData,
|
||||
} )
|
||||
} catch ( error ) {
|
||||
outputNotice( {
|
||||
errorMessage: '',
|
||||
errorContainer: document.getElementById( 'edds-prb-checkout' ),
|
||||
errorContainerReplace: false,
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Payment Method errors.
|
||||
*
|
||||
* @param {Object} event Payment Request event.
|
||||
* @param {Object} error Error.
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
*/
|
||||
function onPaymentMethodError( event, error, checkoutForm ) {
|
||||
// Complete the Payment Request to hide the payment sheet.
|
||||
event.complete( 'success' );
|
||||
|
||||
// Remove spinner.
|
||||
const spinner = checkoutForm.querySelector( '.edds-prb-spinner' );
|
||||
|
||||
if ( spinner ) {
|
||||
spinner.parentNode.removeChild( spinner );
|
||||
}
|
||||
|
||||
// Release loading state.
|
||||
checkoutForm.classList.remove( 'loading' );
|
||||
|
||||
// Add notice.
|
||||
outputNotice( {
|
||||
errorMessage: error.message,
|
||||
errorContainer: document.getElementById( 'edds-prb-checkout' ),
|
||||
errorContainerReplace: false,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles recieving a Payment Method from the Payment Request.
|
||||
*
|
||||
* Adds an item to the cart and processes the Checkout as if we are
|
||||
* in normal Checkout context.
|
||||
*
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} checkoutForm Checkout form.
|
||||
* @param {Object} event paymentmethod event.
|
||||
*/
|
||||
async function onPaymentMethod( paymentRequest, checkoutForm, event ) {
|
||||
try {
|
||||
// Retrieve information from the PRB event.
|
||||
const { paymentMethod, payerEmail, payerName } = event;
|
||||
|
||||
// Loading state. Block interaction.
|
||||
checkoutForm.classList.add( 'loading' );
|
||||
|
||||
// Create and append a spinner.
|
||||
const spinner = document.createElement( 'span' );
|
||||
[ 'edd-loading-ajax', 'edd-loading', 'edds-prb-spinner' ].forEach(
|
||||
( className ) => spinner.classList.add( className )
|
||||
);
|
||||
checkoutForm.appendChild( spinner );
|
||||
|
||||
const data = {
|
||||
email: payerEmail,
|
||||
name: payerName,
|
||||
paymentMethod,
|
||||
context: 'checkout',
|
||||
};
|
||||
|
||||
const tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
// Start the processing.
|
||||
//
|
||||
// Shims $_POST data to align with the standard Checkout context.
|
||||
//
|
||||
// This calls `_edds_process_purchase_form()` server-side which
|
||||
// creates and returns a PaymentIntent -- just like the first step
|
||||
// of a true Checkout.
|
||||
const {
|
||||
intent,
|
||||
intent: {
|
||||
client_secret: clientSecret,
|
||||
object: intentType,
|
||||
},
|
||||
nonce: refreshedNonce,
|
||||
} = await apiRequest( 'edds_prb_ajax_process_checkout', {
|
||||
name: payerName,
|
||||
paymentMethod,
|
||||
form_data: $( '#edd_purchase_form' ).serialize(),
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
} );
|
||||
|
||||
// Update existing nonce value in DOM form data in case data is retrieved
|
||||
// again directly from the DOM.
|
||||
$( '#edd-process-checkout-nonce' ).val( refreshedNonce );
|
||||
|
||||
// Complete the Payment Request to hide the payment sheet.
|
||||
event.complete( 'success' );
|
||||
|
||||
// Confirm the card (SCA, etc).
|
||||
const confirmFunc = 'setup_intent' === intentType
|
||||
? 'confirmCardSetup'
|
||||
: 'confirmCardPayment';
|
||||
|
||||
eddStripe[ confirmFunc ](
|
||||
clientSecret,
|
||||
{
|
||||
payment_method: paymentMethod.id
|
||||
},
|
||||
{
|
||||
handleActions: false,
|
||||
}
|
||||
)
|
||||
.then( ( { error } ) => {
|
||||
// Something went wrong. Alert the Payment Request.
|
||||
if ( error ) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Confirm again after the Payment Request dialog has been hidden.
|
||||
// For cards that do not require further checks this will throw a 400
|
||||
// error (in the Stripe API) and a log console error but not throw
|
||||
// an actual Exception. This can be ignored.
|
||||
//
|
||||
// https://github.com/stripe/stripe-payments-demo/issues/133#issuecomment-632593669
|
||||
eddStripe[ confirmFunc ]( clientSecret )
|
||||
.then( async ( { error } ) => {
|
||||
try {
|
||||
if ( error ) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Create an EDD Payment.
|
||||
const { intent: updatedIntent, nonce } = await createPayment( intent );
|
||||
|
||||
// Complete the EDD Payment with the updated PaymentIntent.
|
||||
await completePayment( updatedIntent, nonce );
|
||||
|
||||
// Redirect on completion.
|
||||
window.location.replace( edd_stripe_vars.successPageUri );
|
||||
|
||||
// Something went wrong, output a notice.
|
||||
} catch ( error ) {
|
||||
onPaymentMethodError( event, error, checkoutForm );
|
||||
}
|
||||
} );
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
onPaymentMethodError( event, error, checkoutForm );
|
||||
} );
|
||||
|
||||
// Something went wrong, output a notice.
|
||||
} catch ( error ) {
|
||||
onPaymentMethodError( event, error, checkoutForm );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a full page reload is needed when applying a discount.
|
||||
*
|
||||
* A 100% discount switches to the "manual" gateway, bypassing the Stripe,
|
||||
* however we are still bound to the Payment Request button and a standard
|
||||
* Purchase button is not present in the DOM to switch back to.add-new-card
|
||||
*
|
||||
* @param {Event} e edd_discount_applied event.
|
||||
* @param {Object} response Discount application response.
|
||||
* @param {int} response.total_plain Cart total after discount.
|
||||
*/
|
||||
function onApplyDiscount( e, { total_plain: total } ) {
|
||||
if ( true === IS_PRB_GATEWAY && 0 === total ) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds purchase link form events.
|
||||
*
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} checkoutForm Checkout form.
|
||||
*/
|
||||
function bindEvents( paymentRequest, checkoutForm ) {
|
||||
const $body = jQuery( document.body );
|
||||
|
||||
// Cart quantities have changed.
|
||||
$body.on( 'edd_quantity_updated', () => onChange( paymentRequest, checkoutForm ) );
|
||||
|
||||
// Discounts have changed.
|
||||
$body.on( 'edd_discount_applied', () => onChange( paymentRequest, checkoutForm ) );
|
||||
$body.on( 'edd_discount_removed', () => onChange( paymentRequest, checkoutForm ) );
|
||||
|
||||
// Handle a PaymentMethod when available.
|
||||
paymentRequest.on( 'paymentmethod', ( event ) => {
|
||||
onPaymentMethod( paymentRequest, checkoutForm, event );
|
||||
} );
|
||||
|
||||
// Handle 100% discounts that require a full gateway refresh.
|
||||
$body.on( 'edd_discount_applied', onApplyDiscount );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts Payment Request buttons (if possible).
|
||||
*
|
||||
* @param {HTMLElement} element Payment Request button mount wrapper.
|
||||
*/
|
||||
function mount( element ) {
|
||||
const { eddStripe } = window;
|
||||
|
||||
const checkoutForm = document.getElementById( 'edd_checkout_form_wrap' );
|
||||
|
||||
try {
|
||||
// Gather initial data.
|
||||
const { 'display-items': displayItems, ...data } = parseDataset( element.dataset );
|
||||
|
||||
// Create a Payment Request object.
|
||||
const paymentRequest = eddStripe.paymentRequest( {
|
||||
// Only requested to prompt full address information collection for Apple Pay.
|
||||
//
|
||||
// On-page name fields are used to update the Easy Digital Downloads Customer.
|
||||
// The Payment Request's Payment Method populate the Customer's Billing Details.
|
||||
//
|
||||
// @link https://stripe.com/docs/js/payment_request/create#stripe_payment_request-options-requestPayerName
|
||||
requestPayerName: true,
|
||||
displayItems,
|
||||
...data,
|
||||
} );
|
||||
|
||||
// Create a Payment Request button.
|
||||
const elements = eddStripe.elements();
|
||||
const prButton = elements.create( 'paymentRequestButton', {
|
||||
paymentRequest: paymentRequest,
|
||||
} );
|
||||
|
||||
const wrapper = document.querySelector( `#${ element.id }` );
|
||||
|
||||
// Check the availability of the Payment Request API.
|
||||
paymentRequest.canMakePayment()
|
||||
// Attempt to mount.
|
||||
.then( function( result ) {
|
||||
// Hide wrapper if nothing can be mounted.
|
||||
if ( ! result ) {
|
||||
return hideAndSwitchGateways();
|
||||
}
|
||||
|
||||
// Hide wrapper if using Apple Pay but in Test Mode.
|
||||
// The verification for Connected accounts in Test Mode is not reliable.
|
||||
if ( true === result.applePay && 'true' === edd_stripe_vars.isTestMode ) {
|
||||
return hideAndSwitchGateways();
|
||||
}
|
||||
|
||||
// Mount.
|
||||
wrapper.style.display = 'block';
|
||||
checkoutForm.classList.add( 'edd-prb--is-active' );
|
||||
prButton.mount( `#${ element.id } .edds-prb__button` );
|
||||
|
||||
// Bind variable pricing/quantity events.
|
||||
bindEvents( paymentRequest, checkoutForm );
|
||||
|
||||
// Handle "Terms of Service" and "Privacy Policy" client validation.
|
||||
prButton.on( 'click', onClick );
|
||||
} );
|
||||
} catch ( error ) {
|
||||
outputNotice( {
|
||||
errorMessage: error.message,
|
||||
errorContainer: document.querySelector( '#edds-prb-checkout' ),
|
||||
errorContainerReplace: false,
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs an initial check for Payment Request support.
|
||||
*
|
||||
* Used when Stripe is not the default gateway (and therefore Express Checkout is not
|
||||
* loaded by default) to do a "background" check of support while a different initial
|
||||
* gateway is loaded.
|
||||
*
|
||||
* @link https://github.com/easydigitaldownloads/edd-stripe/issues/652
|
||||
*/
|
||||
function paymentRequestPrecheck() {
|
||||
const {
|
||||
eddStripe: stripe,
|
||||
edd_stripe_vars: config
|
||||
} = window;
|
||||
|
||||
if ( ! config || ! stripe ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { currency, country, checkoutHasPaymentRequest } = config;
|
||||
|
||||
if ( 'false' === checkoutHasPaymentRequest ) {
|
||||
return;
|
||||
}
|
||||
|
||||
stripe.paymentRequest( {
|
||||
country,
|
||||
currency: currency.toLowerCase(),
|
||||
total: {
|
||||
label: 'Easy Digital Downloads',
|
||||
amount: 100,
|
||||
}
|
||||
} )
|
||||
.canMakePayment()
|
||||
.then( ( result ) => {
|
||||
if ( null === result ) {
|
||||
hideAndSwitchGateways();
|
||||
}
|
||||
|
||||
const checkoutForm = document.getElementById( 'edd_checkout_form_wrap' );
|
||||
checkoutForm.classList.add( 'edd-prb--is-active' );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up Payment Request functionality for single purchase links.
|
||||
*/
|
||||
export function setup() {
|
||||
if ( '1' !== edd_scripts.is_checkout ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts PRB when the gateway has loaded.
|
||||
*
|
||||
* @param {Event} e Gateway loaded event.
|
||||
* @param {string} gateway Gateway ID.
|
||||
*/
|
||||
jQuery( document.body ).on( 'edd_gateway_loaded', ( e, gateway ) => {
|
||||
if ( 'stripe-prb' !== gateway ) {
|
||||
IS_PRB_GATEWAY = false;
|
||||
|
||||
// Always check for Payment Request support if Stripe is active.
|
||||
paymentRequestPrecheck();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const prbEl = document.querySelector( '.edds-prb.edds-prb--checkout' );
|
||||
|
||||
if ( ! prbEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
IS_PRB_GATEWAY = true;
|
||||
|
||||
mount( prbEl );
|
||||
} );
|
||||
}
|
@ -0,0 +1,416 @@
|
||||
/* global edd_stripe_vars, jQuery */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { parseDataset } from './';
|
||||
import { apiRequest, forEach, outputNotice } from 'utils';
|
||||
import { handle as handleIntent } from 'frontend/stripe-elements';
|
||||
import { createPayment, completePayment } from 'frontend/payment-forms';
|
||||
|
||||
/**
|
||||
* Finds the Download ID, Price ID, and quantity values for single Download.
|
||||
*
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
* @return {Object}
|
||||
*/
|
||||
function getDownloadData( purchaseLink ) {
|
||||
let downloadId, priceId = false, quantity = 1;
|
||||
|
||||
// Download ID.
|
||||
const downloadIdEl = purchaseLink.querySelector( '[name="download_id"]' );
|
||||
downloadId = parseFloat( downloadIdEl.value );
|
||||
|
||||
// Price ID.
|
||||
const priceIdEl = purchaseLink.querySelector(
|
||||
`.edd_price_option_${downloadId}:checked`
|
||||
);
|
||||
|
||||
if ( priceIdEl ) {
|
||||
priceId = parseFloat( priceIdEl.value );
|
||||
}
|
||||
|
||||
// Quantity.
|
||||
const quantityEl = purchaseLink.querySelector(
|
||||
'input[name="edd_download_quantity"]'
|
||||
);
|
||||
|
||||
if ( quantityEl ) {
|
||||
quantity = parseFloat( quantityEl.value );
|
||||
}
|
||||
|
||||
return {
|
||||
downloadId,
|
||||
priceId,
|
||||
quantity,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes to the purchase link form by updating the Payment Request object.
|
||||
*
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
*/
|
||||
async function onChange( paymentRequest, purchaseLink ) {
|
||||
const { downloadId, priceId, quantity } = getDownloadData( purchaseLink );
|
||||
|
||||
try {
|
||||
// Calculate and gather price information.
|
||||
const {
|
||||
'display-items': displayItems,
|
||||
...paymentRequestData
|
||||
} = await apiRequest( 'edds_prb_ajax_get_options', {
|
||||
downloadId,
|
||||
priceId,
|
||||
quantity,
|
||||
} )
|
||||
|
||||
// Update the Payment Request with server-side data.
|
||||
paymentRequest.update( {
|
||||
displayItems,
|
||||
...paymentRequestData,
|
||||
} )
|
||||
} catch ( error ) {
|
||||
outputNotice( {
|
||||
errorMessage: '',
|
||||
errorContainer: purchaseLink,
|
||||
errorContainerReplace: false,
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Payment Request amount when the "Custom Amount" input changes.
|
||||
*
|
||||
* @param {HTMLElement} addToCartEl Add to cart button.
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
*/
|
||||
async function onChangeCustomPrice( addToCartEl, paymentRequest, purchaseLink ) {
|
||||
const { price } = addToCartEl.dataset;
|
||||
const { downloadId, priceId, quantity } = getDownloadData( purchaseLink );
|
||||
|
||||
try {
|
||||
// Calculate and gather price information.
|
||||
const {
|
||||
'display-items': displayItems,
|
||||
...paymentRequestData
|
||||
} = await apiRequest( 'edds_prb_ajax_get_options', {
|
||||
downloadId,
|
||||
priceId,
|
||||
quantity,
|
||||
} )
|
||||
|
||||
// Find the "Custom Amount" price.
|
||||
const { is_zero_decimal: isZeroDecimal } = edd_stripe_vars;
|
||||
let amount = parseFloat( price );
|
||||
|
||||
if ( 'false' === isZeroDecimal ) {
|
||||
amount = Math.round( amount * 100 );
|
||||
}
|
||||
|
||||
// Update the Payment Request with the returned server-side data.
|
||||
// Force update the `amount` in all `displayItems` and `total`.
|
||||
//
|
||||
// "Custom Prices" does not support quantities and Payment Requests
|
||||
// do not support taxes so the same amount applies across the board.
|
||||
paymentRequest.update( {
|
||||
displayItems: displayItems.map( ( { label } ) => ( {
|
||||
label,
|
||||
amount,
|
||||
} ) ),
|
||||
...paymentRequestData,
|
||||
total: {
|
||||
label: paymentRequestData.total.label,
|
||||
amount,
|
||||
},
|
||||
} )
|
||||
} catch ( error ) {
|
||||
outputNotice( {
|
||||
errorMessage: '',
|
||||
errorContainer: purchaseLink,
|
||||
errorContainerReplace: false,
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Payment Method errors.
|
||||
*
|
||||
* @param {Object} event Payment Request event.
|
||||
* @param {Object} error Error.
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
*/
|
||||
function onPaymentMethodError( event, error, purchaseLink ) {
|
||||
// Complete the Payment Request to hide the payment sheet.
|
||||
event.complete( 'success' );
|
||||
|
||||
// Release loading state.
|
||||
purchaseLink.classList.remove( 'loading' );
|
||||
|
||||
outputNotice( {
|
||||
errorMessage: error.message,
|
||||
errorContainer: purchaseLink,
|
||||
errorContainerReplace: false,
|
||||
} );
|
||||
|
||||
// Item is in the cart at this point, so change the Purchase button to Checkout.
|
||||
//
|
||||
// Using jQuery which will preserve the previously set display value in order
|
||||
// to provide better theme compatibility.
|
||||
jQuery( 'a.edd-add-to-cart', purchaseLink ).hide();
|
||||
jQuery( '.edd_download_quantity_wrapper', purchaseLink ).hide();
|
||||
jQuery( '.edd_price_options', purchaseLink ).hide();
|
||||
jQuery( '.edd_go_to_checkout', purchaseLink )
|
||||
.show().removeAttr( 'data-edd-loading' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles recieving a Payment Method from the Payment Request.
|
||||
*
|
||||
* Adds an item to the cart and processes the Checkout as if we are
|
||||
* in normal Checkout context.
|
||||
*
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
* @param {Object} event paymentmethod event.
|
||||
*/
|
||||
async function onPaymentMethod( paymentRequest, purchaseLink, event ) {
|
||||
try {
|
||||
// Retrieve the latest data (price ID, quantity, etc).
|
||||
const { downloadId, priceId, quantity } = getDownloadData( purchaseLink );
|
||||
|
||||
// Retrieve information from the PRB event.
|
||||
const { paymentMethod, payerEmail, payerName } = event;
|
||||
|
||||
const tokenInput = jQuery( '#edd-process-stripe-token-' + downloadId );
|
||||
|
||||
// Start the processing.
|
||||
//
|
||||
// Adds the single Download to the cart and then shims $_POST
|
||||
// data to align with the standard Checkout context.
|
||||
//
|
||||
// This calls `_edds_process_purchase_form()` server-side which
|
||||
// creates and returns a PaymentIntent -- just like the first step
|
||||
// of a true Checkout.
|
||||
const {
|
||||
intent,
|
||||
intent: {
|
||||
client_secret: clientSecret,
|
||||
object: intentType,
|
||||
}
|
||||
} = await apiRequest( 'edds_prb_ajax_process_checkout', {
|
||||
email: payerEmail,
|
||||
name: payerName,
|
||||
paymentMethod,
|
||||
downloadId,
|
||||
priceId,
|
||||
quantity,
|
||||
context: 'download',
|
||||
post_data: jQuery( purchaseLink ).serialize(),
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
} );
|
||||
|
||||
// Complete the Payment Request to hide the payment sheet.
|
||||
event.complete( 'success' );
|
||||
|
||||
// Loading state. Block interaction.
|
||||
purchaseLink.classList.add( 'loading' );
|
||||
|
||||
// Confirm the card (SCA, etc).
|
||||
const confirmFunc = 'setup_intent' === intentType
|
||||
? 'confirmCardSetup'
|
||||
: 'confirmCardPayment';
|
||||
|
||||
eddStripe[ confirmFunc ](
|
||||
clientSecret,
|
||||
{
|
||||
payment_method: paymentMethod.id
|
||||
},
|
||||
{
|
||||
handleActions: false,
|
||||
}
|
||||
)
|
||||
.then( ( { error } ) => {
|
||||
// Something went wrong. Alert the Payment Request.
|
||||
if ( error ) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Confirm again after the Payment Request dialog has been hidden.
|
||||
// For cards that do not require further checks this will throw a 400
|
||||
// error (in the Stripe API) and a log console error but not throw
|
||||
// an actual Exception. This can be ignored.
|
||||
//
|
||||
// https://github.com/stripe/stripe-payments-demo/issues/133#issuecomment-632593669
|
||||
eddStripe[ confirmFunc ]( clientSecret )
|
||||
.then( async ( { error } ) => {
|
||||
try {
|
||||
if ( error ) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Create an EDD Payment.
|
||||
const { intent: updatedIntent, nonce } = await createPayment( intent );
|
||||
|
||||
// Complete the EDD Payment with the updated PaymentIntent.
|
||||
await completePayment( updatedIntent, nonce );
|
||||
|
||||
// Redirect on completion.
|
||||
window.location.replace( edd_stripe_vars.successPageUri );
|
||||
|
||||
// Something went wrong, output a notice.
|
||||
} catch ( error ) {
|
||||
onPaymentMethodError( event, error, purchaseLink );
|
||||
}
|
||||
} );
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
onPaymentMethodError( event, error, purchaseLink );
|
||||
} );
|
||||
|
||||
// Something went wrong, output a notice.
|
||||
} catch ( error ) {
|
||||
onPaymentMethodError( event, error, purchaseLink );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for changes to the "Add to Cart" button.
|
||||
*
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
*/
|
||||
function observeAddToCartChanges( paymentRequest, purchaseLink ) {
|
||||
const addToCartEl = purchaseLink.querySelector( '.edd-add-to-cart' );
|
||||
|
||||
if ( ! addToCartEl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new MutationObserver( ( mutations ) => {
|
||||
mutations.forEach( ( { type, attributeName, target } ) => {
|
||||
if ( type !== 'attributes' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the Payment Request if the price has changed.
|
||||
// Used for "Custom Prices" extension.
|
||||
if ( 'data-price' === attributeName ) {
|
||||
onChangeCustomPrice( target, paymentRequest, purchaseLink );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
observer.observe( addToCartEl, {
|
||||
attributes: true,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds purchase link form events.
|
||||
*
|
||||
* @param {PaymentRequest} paymentRequest Payment Request object.
|
||||
* @param {HTMLElement} purchaseLink Purchase link form.
|
||||
*/
|
||||
function bindEvents( paymentRequest, purchaseLink ) {
|
||||
// Price option change.
|
||||
const priceOptionsEls = purchaseLink.querySelectorAll( '.edd_price_options input[type="radio"]' );
|
||||
|
||||
forEach( priceOptionsEls, ( priceOption ) => {
|
||||
priceOption.addEventListener( 'change', () => onChange( paymentRequest, purchaseLink ) );
|
||||
} );
|
||||
|
||||
// Quantity change.
|
||||
const quantityEl = purchaseLink.querySelector( 'input[name="edd_download_quantity"]' );
|
||||
|
||||
if ( quantityEl ) {
|
||||
quantityEl.addEventListener( 'change', () => onChange( paymentRequest, purchaseLink ) );
|
||||
}
|
||||
|
||||
// Changes to "Add to Cart" button.
|
||||
observeAddToCartChanges( paymentRequest, purchaseLink );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts Payment Request buttons (if possible).
|
||||
*
|
||||
* @param {HTMLElement} element Payment Request button mount wrapper.
|
||||
*/
|
||||
function mount( element ) {
|
||||
const { eddStripe } = window;
|
||||
|
||||
try {
|
||||
// Gather initial data.
|
||||
const { 'display-items': displayItems, ...data } = parseDataset( element.dataset );
|
||||
|
||||
// Find the purchase link form.
|
||||
const purchaseLink = element.closest(
|
||||
'.edd_download_purchase_form'
|
||||
);
|
||||
|
||||
// Create a Payment Request object.
|
||||
const paymentRequest = eddStripe.paymentRequest( {
|
||||
// Requested to prompt full address information collection for Apple Pay.
|
||||
//
|
||||
// Collected email address is used to create/update Easy Digital Downloads Customer.
|
||||
//
|
||||
// @link https://stripe.com/docs/js/payment_request/create#stripe_payment_request-options-requestPayerName
|
||||
requestPayerEmail: true,
|
||||
displayItems,
|
||||
...data,
|
||||
} );
|
||||
|
||||
// Create a Payment Request button.
|
||||
const elements = eddStripe.elements();
|
||||
const prButton = elements.create( 'paymentRequestButton', {
|
||||
paymentRequest: paymentRequest,
|
||||
} );
|
||||
|
||||
const wrapper = document.querySelector( `#${ element.id }` );
|
||||
|
||||
// Check the availability of the Payment Request API.
|
||||
paymentRequest.canMakePayment()
|
||||
// Attempt to mount.
|
||||
.then( function( result ) {
|
||||
// Hide wrapper if nothing can be mounted.
|
||||
if ( ! result ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide wrapper if using Apple Pay but in Test Mode.
|
||||
// The verification for Connected accounts in Test Mode is not reliable.
|
||||
if ( true === result.applePay && 'true' === edd_stripe_vars.isTestMode ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mount.
|
||||
wrapper.style.display = 'block';
|
||||
purchaseLink.classList.add( 'edd-prb--is-active' );
|
||||
prButton.mount( `#${ element.id } .edds-prb__button` );
|
||||
|
||||
// Bind variable pricing/quantity events.
|
||||
bindEvents( paymentRequest, purchaseLink );
|
||||
} );
|
||||
|
||||
// Handle a PaymentMethod when available.
|
||||
paymentRequest.on( 'paymentmethod', ( event ) => {
|
||||
onPaymentMethod( paymentRequest, purchaseLink, event );
|
||||
} );
|
||||
} catch ( error ) {
|
||||
outputNotice( {
|
||||
errorMessage: error.message,
|
||||
errorContainer: purchaseLink,
|
||||
errorContainerReplace: false,
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up Payment Request functionality for single purchase links.
|
||||
*/
|
||||
export function setup() {
|
||||
forEach( document.querySelectorAll( '.edds-prb.edds-prb--download' ), mount );
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
export { setup as setupDownload } from './download.js';
|
||||
export { setup as setupCheckout } from './checkout.js';
|
||||
|
||||
/**
|
||||
* Parses an HTML dataset and decodes JSON values.
|
||||
*
|
||||
* @param {Object} dataset HTML data attributes.
|
||||
* @return {Object}
|
||||
*/
|
||||
export function parseDataset( dataset ) {
|
||||
let data = {};
|
||||
|
||||
for ( const [ key, value ] of Object.entries( dataset ) ) {
|
||||
let parsedValue = value;
|
||||
|
||||
try {
|
||||
parsedValue = JSON.parse( value );
|
||||
} catch ( e ) {}
|
||||
|
||||
data[ key ] = parsedValue;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { paymentMethodActions } from './payment-method-actions.js';
|
||||
import { paymentForm } from './payment-form.js';
|
||||
|
||||
export function setup() {
|
||||
if ( ! document.getElementById( 'edd-stripe-manage-cards' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
paymentMethodActions();
|
||||
paymentForm();
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/* global edd_stripe_vars, location */
|
||||
|
||||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
import {
|
||||
createPaymentForm as createElementsPaymentForm,
|
||||
getBillingDetails
|
||||
} from 'frontend/stripe-elements';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
hasValidInputs,
|
||||
triggerBrowserValidation,
|
||||
generateNotice,
|
||||
forEach
|
||||
} from 'utils';
|
||||
|
||||
/**
|
||||
* Binds events and sets up "Add New" form.
|
||||
*/
|
||||
export function paymentForm() {
|
||||
// Mount Elements.
|
||||
createElementsPaymentForm( window.eddStripe.elements() );
|
||||
|
||||
// Toggles and submission.
|
||||
document.querySelector( '.edd-stripe-add-new' ).addEventListener( 'click', onToggleForm );
|
||||
document.getElementById( 'edd-stripe-add-new-cancel' ).addEventListener( 'click', onToggleForm );
|
||||
document.getElementById( 'edd-stripe-add-new-card' ).addEventListener( 'submit', onAddPaymentMethod );
|
||||
|
||||
// Set "Card Name" field as required by HTML5
|
||||
document.getElementById( 'card_name' ).required = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles toggling of "Add New" form button and submission.
|
||||
*
|
||||
* @param {Event} e click event.
|
||||
*/
|
||||
function onToggleForm( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = document.getElementById( 'edd-stripe-add-new-card' );
|
||||
const formFields = form.querySelector( '.edd-stripe-add-new-card' );
|
||||
const isFormVisible = 'block' === formFields.style.display;
|
||||
|
||||
const cancelButton = form.querySelector( '#edd-stripe-add-new-cancel' );
|
||||
|
||||
// Trigger a `submit` event.
|
||||
if ( isFormVisible && cancelButton !== e.target ) {
|
||||
const submitEvent = document.createEvent( 'Event' );
|
||||
|
||||
submitEvent.initEvent( 'submit', true, true );
|
||||
form.dispatchEvent( submitEvent );
|
||||
// Toggle form.
|
||||
} else {
|
||||
formFields.style.display = ! isFormVisible ? 'block' : 'none';
|
||||
cancelButton.style.display = ! isFormVisible ? 'inline-block' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new Source to the Customer.
|
||||
*
|
||||
* @param {Event} e submit event.
|
||||
*/
|
||||
function onAddPaymentMethod( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = e.target;
|
||||
|
||||
if ( ! hasValidInputs( form ) ) {
|
||||
triggerBrowserValidation( form );
|
||||
} else {
|
||||
try {
|
||||
disableForm();
|
||||
|
||||
createPaymentMethod( form )
|
||||
.then( addPaymentMethod )
|
||||
.catch( ( error ) => {
|
||||
handleNotice( error );
|
||||
enableForm();
|
||||
} );
|
||||
} catch ( error ) {
|
||||
handleNotice( error );
|
||||
enableForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a PaymentMethod.
|
||||
*
|
||||
* @param {Object} paymentMethod
|
||||
*/
|
||||
export function addPaymentMethod( paymentMethod ) {
|
||||
var tokenInput = document.getElementById( '#edd-process-stripe-token' );
|
||||
|
||||
apiRequest( 'edds_add_payment_method', {
|
||||
payment_method_id: paymentMethod.id,
|
||||
nonce: document.getElementById( 'edd-stripe-add-card-nonce' ).value,
|
||||
timestamp: tokenInput ? tokenInput.dataset.timestamp : '',
|
||||
token: tokenInput ? tokenInput.dataset.token : '',
|
||||
} )
|
||||
/**
|
||||
* Shows an error when the API request fails.
|
||||
*
|
||||
* @param {Object} response API Request response.
|
||||
*/
|
||||
.fail( handleNotice )
|
||||
/**
|
||||
* Shows a success notice and automatically redirect.
|
||||
*
|
||||
* @param {Object} response API Request response.
|
||||
*/
|
||||
.done( function( response ) {
|
||||
handleNotice( response, 'success' );
|
||||
|
||||
// Automatically redirect on success.
|
||||
setTimeout( function() {
|
||||
location.reload();
|
||||
}, 1500 );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PaymentMethod from a card and billing form.
|
||||
*
|
||||
* @param {HTMLElement} billingForm Form with billing fields to retrieve data from.
|
||||
* @return {Object} Stripe PaymentMethod.
|
||||
*/
|
||||
function createPaymentMethod( billingForm ) {
|
||||
return window.eddStripe
|
||||
// Create a PaymentMethod with stripe.js
|
||||
.createPaymentMethod(
|
||||
'card',
|
||||
window.eddStripe.cardElement,
|
||||
{
|
||||
billing_details: getBillingDetails( billingForm ),
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Handles PaymentMethod creation response.
|
||||
*
|
||||
* @param {Object} result PaymentMethod creation result.
|
||||
*/
|
||||
.then( function( result ) {
|
||||
if ( result.error ) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
return result.paymentMethod;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables "Add New" form.
|
||||
*/
|
||||
function disableForm() {
|
||||
const submit = document.querySelector( '.edd-stripe-add-new' );
|
||||
|
||||
submit.value = submit.dataset.loading;
|
||||
submit.disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables "Add New" form.
|
||||
*/
|
||||
function enableForm() {
|
||||
const submit = document.querySelector( '.edd-stripe-add-new' );
|
||||
|
||||
submit.value = submit.dataset.submit;
|
||||
submit.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a notice (success or error) for card actions.
|
||||
*
|
||||
* @param {Object} error Error with message to output.
|
||||
* @param {string} type Notice type.
|
||||
*/
|
||||
export function handleNotice( error, type = 'error' ) {
|
||||
// Create the new notice.
|
||||
const notice = generateNotice(
|
||||
( error && error.message ) ? error.message : edd_stripe_vars.generic_error,
|
||||
type
|
||||
);
|
||||
|
||||
// Hide previous notices.
|
||||
forEach( document.querySelectorAll( '.edd-stripe-alert' ), function( alert ) {
|
||||
alert.remove();
|
||||
} );
|
||||
|
||||
// Show new notice.
|
||||
document.querySelector( '.edd-stripe-add-card-actions' )
|
||||
.insertBefore( notice, document.querySelector( '.edd-stripe-add-new' ) );
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/* global edd_stripe_vars, location */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { apiRequest, generateNotice, fieldValueOrNull, forEach } from 'utils'; // eslint-disable-line @wordpress/dependency-group
|
||||
|
||||
/**
|
||||
* Binds events for card actions.
|
||||
*/
|
||||
export function paymentMethodActions() {
|
||||
// Update.
|
||||
forEach( document.querySelectorAll( '.edd-stripe-update-card' ), function( updateButton ) {
|
||||
updateButton.addEventListener( 'click', onToggleUpdateForm );
|
||||
} );
|
||||
|
||||
forEach( document.querySelectorAll( '.edd-stripe-cancel-update' ), function( cancelButton ) {
|
||||
cancelButton.addEventListener( 'click', onToggleUpdateForm );
|
||||
} );
|
||||
|
||||
forEach( document.querySelectorAll( '.card-update-form' ), function( updateButton ) {
|
||||
updateButton.addEventListener( 'submit', onUpdatePaymentMethod );
|
||||
} );
|
||||
|
||||
// Delete.
|
||||
forEach( document.querySelectorAll( '.edd-stripe-delete-card' ), function( deleteButton ) {
|
||||
deleteButton.addEventListener( 'click', onDeletePaymentMethod );
|
||||
} );
|
||||
|
||||
// Set Default.
|
||||
forEach( document.querySelectorAll( '.edd-stripe-default-card' ), function( setDefaultButton ) {
|
||||
setDefaultButton.addEventListener( 'click', onSetDefaultPaymentMethod );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a generic Payment Method action (set default, update, delete).
|
||||
*
|
||||
* @param {string} action Payment action.
|
||||
* @param {string} paymentMethodId PaymentMethod ID.
|
||||
* @param {null|Object} data Additional AJAX data.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
function paymentMethodAction( action, paymentMethodId, data = {} ) {
|
||||
var tokenInput = document.getElementById( 'edd-process-stripe-token-' + paymentMethodId );
|
||||
data.timestamp = tokenInput ? tokenInput.dataset.timestamp : '';
|
||||
data.token = tokenInput ? tokenInput.dataset.token : '';
|
||||
|
||||
return apiRequest( action, {
|
||||
payment_method: paymentMethodId,
|
||||
nonce: document.getElementById( 'card_update_nonce_' + paymentMethodId ).value,
|
||||
...data,
|
||||
} )
|
||||
/**
|
||||
* Shows an error when the API request fails.
|
||||
*
|
||||
* @param {Object} response API Request response.
|
||||
*/
|
||||
.fail( function( response ) {
|
||||
handleNotice( paymentMethodId, response );
|
||||
} )
|
||||
/**
|
||||
* Shows a success notice and automatically redirect.
|
||||
*
|
||||
* @param {Object} response API Request response.
|
||||
*/
|
||||
.done( function( response ) {
|
||||
handleNotice( paymentMethodId, response, 'success' );
|
||||
|
||||
// Automatically redirect on success.
|
||||
setTimeout( function() {
|
||||
location.reload();
|
||||
}, 1500 );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
function onToggleUpdateForm( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const source = e.target.dataset.source;
|
||||
|
||||
const form = document.getElementById( source + '-update-form' );
|
||||
const cardActionsEl = document.getElementById( source + '-card-actions' );
|
||||
const isFormVisible = 'block' === form.style.display;
|
||||
|
||||
form.style.display = ! isFormVisible ? 'block' : 'none';
|
||||
cardActionsEl.style.display = ! isFormVisible ? 'none' : 'block';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
function onUpdatePaymentMethod( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = e.target;
|
||||
const data = {};
|
||||
|
||||
// Gather form data.
|
||||
const updateFields = [
|
||||
'address_city',
|
||||
'address_country',
|
||||
'address_line1',
|
||||
'address_line2',
|
||||
'address_zip',
|
||||
'address_state',
|
||||
'exp_month',
|
||||
'exp_year',
|
||||
];
|
||||
|
||||
updateFields.forEach( function( fieldName ) {
|
||||
const field = form.querySelector( '[name="' + fieldName + '"]' );
|
||||
data[ fieldName ] = fieldValueOrNull( field );
|
||||
} );
|
||||
|
||||
const submitButton = form.querySelector( 'input[type="submit"]' );
|
||||
|
||||
submitButton.disabled = true;
|
||||
submitButton.value = submitButton.dataset.loading;
|
||||
|
||||
paymentMethodAction( 'edds_update_payment_method', e.target.dataset.source, data )
|
||||
.fail( function( response ) {
|
||||
submitButton.disabled = false;
|
||||
submitButton.value = submitButton.dataset.submit;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
function onDeletePaymentMethod( e ) {
|
||||
e.preventDefault();
|
||||
const loading = '<span class="edd-loading-ajax edd-loading"></span>';
|
||||
const linkText = e.target.innerText;
|
||||
e.target.innerHTML = loading;
|
||||
|
||||
paymentMethodAction( 'edds_delete_payment_method', e.target.dataset.source )
|
||||
.fail( function( response ) {
|
||||
e.target.innerText = linkText;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
function onSetDefaultPaymentMethod( e ) {
|
||||
e.preventDefault();
|
||||
const loading = '<span class="edd-loading-ajax edd-loading"></span>';
|
||||
const linkText = e.target.innerText;
|
||||
e.target.innerHTML = loading;
|
||||
|
||||
paymentMethodAction( 'edds_set_payment_method_default', e.target.dataset.source )
|
||||
.fail( function( response ) {
|
||||
e.target.innerText = linkText;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a notice (success or error) for card actions.
|
||||
*
|
||||
* @param {string} paymentMethodId
|
||||
* @param {Object} error Error with message to output.
|
||||
* @param {string} type Notice type.
|
||||
*/
|
||||
export function handleNotice( paymentMethodId, error, type = 'error' ) {
|
||||
// Create the new notice.
|
||||
const notice = generateNotice(
|
||||
( error && error.message ) ? error.message : edd_stripe_vars.generic_error,
|
||||
type
|
||||
);
|
||||
|
||||
// Hide previous notices.
|
||||
forEach( document.querySelectorAll( '.edd-stripe-alert' ), function( alert ) {
|
||||
alert.remove();
|
||||
} );
|
||||
|
||||
const item = document.getElementById( paymentMethodId + '_card_item' );
|
||||
|
||||
// Show new notice.
|
||||
item.insertBefore( notice, item.querySelector( '.card-details' ) );
|
||||
}
|
@ -0,0 +1,312 @@
|
||||
/* global $, edd_stripe_vars */
|
||||
|
||||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
import {
|
||||
generateNotice,
|
||||
fieldValueOrNull,
|
||||
forEach
|
||||
} from 'utils'; // eslint-disable-line @wordpress/dependency-group
|
||||
|
||||
// Intents.
|
||||
export * from './intents.js';
|
||||
|
||||
const DEFAULT_ELEMENTS = {
|
||||
'card': '#edd-stripe-card-element',
|
||||
}
|
||||
|
||||
const DEFAULT_SPLIT_ELEMENTS = {
|
||||
'cardNumber': '#edd-stripe-card-element',
|
||||
'cardExpiry': '#edd-stripe-card-exp-element',
|
||||
'cardCvc': '#edd-stripe-card-cvc-element',
|
||||
}
|
||||
|
||||
let ELEMENTS_OPTIONS = { ...edd_stripe_vars.elementsOptions };
|
||||
|
||||
/**
|
||||
* Mounts Elements based on payment form configuration.
|
||||
*
|
||||
* Assigns a `cardElement` object to the global `eddStripe` object
|
||||
* that can be used to collect card data for tokenization.
|
||||
*
|
||||
* Integrations (such as Recurring) should pass a configuration of Element
|
||||
* types and specific HTML IDs to mount based on settings and form markup
|
||||
* to avoid attempting to mount to the same `HTMLElement`.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param {Object} elementsInstance Stripe Elements instance.
|
||||
* @return {Element} The last Stripe Element to be mounted.
|
||||
*/
|
||||
export function createPaymentForm( elementsInstance, elements ) {
|
||||
let mountedEl;
|
||||
|
||||
if ( ! elements ) {
|
||||
elements = ( 'true' === edd_stripe_vars.elementsSplitFields )
|
||||
? DEFAULT_SPLIT_ELEMENTS
|
||||
: DEFAULT_ELEMENTS;
|
||||
}
|
||||
|
||||
forEach( elements, ( selector, element ) => {
|
||||
mountedEl = createAndMountElement( elementsInstance, selector, element );
|
||||
} );
|
||||
|
||||
// Make at least one Element available globally.
|
||||
window.eddStripe.cardElement = mountedEl;
|
||||
|
||||
return mountedEl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and returns an object of styles that can be used to change the appearance
|
||||
* of the Stripe Elements iFrame based on existing form styles.
|
||||
*
|
||||
* Styles that can be applied to the current DOM are injected to the page via
|
||||
* a <style> element.
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js/reference#the-elements-object
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
function generateElementStyles() {
|
||||
// Try to mimick existing input styles.
|
||||
const cardNameEl = document.querySelector( '.card-name.edd-input' );
|
||||
|
||||
if ( ! cardNameEl ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inputStyles = window.getComputedStyle( cardNameEl );
|
||||
|
||||
// Inject inline CSS instead of applying to the Element so it can be overwritten.
|
||||
if ( ! document.getElementById( 'edds-stripe-element-styles' ) ) {
|
||||
const styleTag = document.createElement( 'style' );
|
||||
|
||||
styleTag.innerHTML = `
|
||||
.edd-stripe-card-element.StripeElement,
|
||||
.edd-stripe-card-exp-element.StripeElement,
|
||||
.edd-stripe-card-cvc-element.StripeElement {
|
||||
background-color: ${ inputStyles.getPropertyValue( 'background-color' ) };
|
||||
|
||||
${
|
||||
[ 'top', 'right', 'bottom', 'left' ]
|
||||
.map( ( dir ) => (
|
||||
`border-${ dir }-color: ${ inputStyles.getPropertyValue( `border-${ dir }-color` ) };
|
||||
border-${ dir }-width: ${ inputStyles.getPropertyValue( `border-${ dir }-width` ) };
|
||||
border-${ dir }-style: ${ inputStyles.getPropertyValue( `border-${ dir }-style` ) };
|
||||
padding-${ dir }: ${ inputStyles.getPropertyValue( `padding-${ dir }` ) };`
|
||||
) )
|
||||
.join( '' )
|
||||
}
|
||||
${
|
||||
[ 'top-right', 'bottom-right', 'bottom-left', 'top-left' ]
|
||||
.map( ( dir ) => (
|
||||
`border-${ dir }-radius: ${ inputStyles.getPropertyValue( 'border-top-right-radius' ) };`
|
||||
) )
|
||||
.join( '' )
|
||||
}
|
||||
}`
|
||||
// Remove whitespace.
|
||||
.replace( /\s/g, '' );
|
||||
|
||||
styleTag.id = 'edds-stripe-element-styles';
|
||||
|
||||
document.body.appendChild( styleTag );
|
||||
}
|
||||
|
||||
return {
|
||||
base: {
|
||||
color: inputStyles.getPropertyValue( 'color' ),
|
||||
fontFamily: inputStyles.getPropertyValue( 'font-family' ),
|
||||
fontSize: inputStyles.getPropertyValue( 'font-size' ),
|
||||
fontWeight: inputStyles.getPropertyValue( 'font-weight' ),
|
||||
fontSmoothing: inputStyles.getPropertyValue( '-webkit-font-smoothing' ),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts an Elements Card to the DOM and adds event listeners to submission.
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js/reference#the-elements-object
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param {Elements} elementsInstance Stripe Elements instance.
|
||||
* @param {string} selector Selector to mount Element on.
|
||||
* @return {Element|undefined} Stripe Element.
|
||||
*/
|
||||
function createAndMountElement( elementsInstance, selector, element ) {
|
||||
const el = document.querySelector( selector );
|
||||
|
||||
if ( ! el ) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
ELEMENTS_OPTIONS.style = jQuery.extend(
|
||||
true,
|
||||
{},
|
||||
generateElementStyles(),
|
||||
ELEMENTS_OPTIONS.style
|
||||
);
|
||||
|
||||
// Remove hidePostalCode if not using a combined `card` Element.
|
||||
if ( 'cardNumber' === element && ELEMENTS_OPTIONS.hasOwnProperty( 'hidePostalCode' ) ) {
|
||||
delete ELEMENTS_OPTIONS.hidePostalCode;
|
||||
}
|
||||
|
||||
// Remove unused parameter from options.
|
||||
delete ELEMENTS_OPTIONS.i18n;
|
||||
|
||||
const stripeElement = elementsInstance
|
||||
.create( element, ELEMENTS_OPTIONS );
|
||||
|
||||
stripeElement
|
||||
.addEventListener( 'change', ( event ) => {
|
||||
handleElementError( event, el );
|
||||
handleCardBrandIcon( event );
|
||||
} )
|
||||
.mount( el );
|
||||
|
||||
return stripeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts an Elements Card to the DOM and adds event listeners to submission.
|
||||
*
|
||||
* @since 2.7.0
|
||||
* @since 2.8.0 Deprecated
|
||||
*
|
||||
* @deprecated Use createPaymentForm() to mount specific elements.
|
||||
*
|
||||
* @param {Elements} elementsInstance Stripe Elements instance.
|
||||
* @param {string} toMount Selector to mount Element on.
|
||||
* @return {Element} Stripe Element.
|
||||
*/
|
||||
export function mountCardElement( elementsInstance, toMount = '#edd-stripe-card-element' ) {
|
||||
const mountedEl = createPaymentForm( elementsInstance, {
|
||||
'card': toMount,
|
||||
} );
|
||||
|
||||
// Hide split card details fields because any integration that is using this
|
||||
// directly has not properly implemented split fields.
|
||||
const splitFields = document.getElementById( 'edd-card-details-wrap' );
|
||||
|
||||
if ( splitFields ) {
|
||||
splitFields.style.display = 'none';
|
||||
}
|
||||
|
||||
return mountedEl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles error output for Elements Card.
|
||||
*
|
||||
* @param {Event} event Change event on the Card Element.
|
||||
* @param {HTMLElement} el HTMLElement the Stripe Element is being mounted on.
|
||||
*/
|
||||
function handleElementError( event, el ) {
|
||||
const newCardContainer = el.closest( '.edd-stripe-new-card' );
|
||||
const errorsContainer = newCardContainer.querySelector( '#edd-stripe-card-errors' );
|
||||
|
||||
// Only show one error at once.
|
||||
errorsContainer.innerHTML = '';
|
||||
|
||||
if ( event.error ) {
|
||||
const { code, message } = event.error;
|
||||
const { elementsOptions: { i18n: { errorMessages } } } = window.edd_stripe_vars;
|
||||
|
||||
const localizedMessage = errorMessages[ code ] ? errorMessages[ code ] : message;
|
||||
|
||||
errorsContainer.appendChild( generateNotice( localizedMessage ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates card brand icon if using a split form.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param {Event} event Change event on the Card Element.
|
||||
*/
|
||||
function handleCardBrandIcon( event ) {
|
||||
const {
|
||||
brand,
|
||||
elementType,
|
||||
} = event;
|
||||
|
||||
if ( 'cardNumber' !== event.elementType ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cardTypeEl = document.querySelector( '.card-type' );
|
||||
|
||||
if ( 'unknown' === brand ) {
|
||||
cardTypeEl.className = 'card-type';
|
||||
} else {
|
||||
cardTypeEl.classList.add( brand );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves (or creates) a PaymentMethod.
|
||||
*
|
||||
* @param {HTMLElement} billingDetailsForm Form to find data from.
|
||||
* @return {Object} PaymentMethod ID and if it previously existed.
|
||||
*/
|
||||
export function getPaymentMethod( billingDetailsForm, cardElement ) {
|
||||
const selectedPaymentMethod = $( 'input[name="edd_stripe_existing_card"]:checked' );
|
||||
|
||||
// An existing PaymentMethod is selected.
|
||||
if ( selectedPaymentMethod.length > 0 && 'new' !== selectedPaymentMethod.val() ) {
|
||||
return Promise.resolve( {
|
||||
id: selectedPaymentMethod.val(),
|
||||
exists: true,
|
||||
} );
|
||||
}
|
||||
|
||||
// Create a PaymentMethod using the Element data.
|
||||
return window.eddStripe
|
||||
.createPaymentMethod(
|
||||
'card',
|
||||
cardElement,
|
||||
{
|
||||
billing_details: getBillingDetails( billingDetailsForm ),
|
||||
}
|
||||
)
|
||||
.then( function( result ) {
|
||||
if ( result.error ) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
return {
|
||||
id: result.paymentMethod.id,
|
||||
exists: false,
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves billing details from the Billing Details sections of a form.
|
||||
*
|
||||
* @param {HTMLElement} form Form to find data from.
|
||||
* @return {Object} Billing details
|
||||
*/
|
||||
export function getBillingDetails( form ) {
|
||||
return {
|
||||
// @todo add Phone
|
||||
// @todo add Email
|
||||
name: fieldValueOrNull( form.querySelector( '.card-name' ) ),
|
||||
address: {
|
||||
line1: fieldValueOrNull( form.querySelector( '.card-address' ) ),
|
||||
line2: fieldValueOrNull( form.querySelector( '.card-address-2' ) ),
|
||||
city: fieldValueOrNull( form.querySelector( '.card-city' ) ),
|
||||
state: fieldValueOrNull( form.querySelector( '.card_state' ) ),
|
||||
postal_code: fieldValueOrNull( form.querySelector( '.card-zip' ) ),
|
||||
country: fieldValueOrNull( form.querySelector( '#billing_country' ) ),
|
||||
},
|
||||
};
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/* global jQuery */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { apiRequest } from 'utils'; // eslint-disable-line @wordpress/dependency-group
|
||||
|
||||
/**
|
||||
* Retrieve a PaymentIntent.
|
||||
*
|
||||
* @param {string} intentId Intent ID.
|
||||
* @param {string} intentType Intent type. payment_intent or setup_intent.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function retrieve( intentId, intentType = 'payment_intent' ) {
|
||||
const form = $( window.eddStripe.cardElement._parent ).closest( 'form' ),
|
||||
tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
return apiRequest( 'edds_get_intent', {
|
||||
intent_id: intentId,
|
||||
intent_type: intentType,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
form_data: form.serialize(),
|
||||
} )
|
||||
// Returns just the PaymentIntent object.
|
||||
.then( function( response ) {
|
||||
return response.intent;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm a PaymentIntent.
|
||||
*
|
||||
* @param {Object} intent Stripe PaymentIntent or SetupIntent.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function confirm( intent ) {
|
||||
const form = $( window.eddStripe.cardElement._parent ).closest( 'form' ),
|
||||
tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
return apiRequest( 'edds_confirm_intent', {
|
||||
intent_id: intent.id,
|
||||
intent_type: intent.object,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
form_data: form.serialize(),
|
||||
} )
|
||||
// Returns just the PaymentIntent object for easier reprocessing.
|
||||
.then( function( response ) {
|
||||
return response.intent;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture a PaymentIntent.
|
||||
*
|
||||
* @param {Object} intent Stripe PaymentIntent or SetupIntent.
|
||||
* @param {Object} data Extra data to pass to the intent action.
|
||||
* @param {string} refreshedNonce A refreshed nonce that might be needed if the
|
||||
* user logged in.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function capture( intent, data, refreshedNonce ) {
|
||||
const form = $( window.eddStripe.cardElement._parent ).closest( 'form' );
|
||||
|
||||
if ( 'requires_capture' !== intent.status ) {
|
||||
return Promise.resolve( intent );
|
||||
}
|
||||
|
||||
let formData = form.serialize(),
|
||||
tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
// Add the refreshed nonce if available.
|
||||
if ( refreshedNonce ) {
|
||||
formData += `&edd-process-checkout-nonce=${ refreshedNonce }`;
|
||||
}
|
||||
|
||||
return apiRequest( 'edds_capture_intent', {
|
||||
intent_id: intent.id,
|
||||
intent_type: intent.object,
|
||||
form_data: formData,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
...data,
|
||||
} )
|
||||
// Returns just the PaymentIntent object for easier reprocessing.
|
||||
.then( function( response ) {
|
||||
return response.intent;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a PaymentIntent.
|
||||
*
|
||||
* @param {Object} intent Stripe PaymentIntent or SetupIntent.
|
||||
* @param {Object} data PaymentIntent data to update.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function update( intent, data ) {
|
||||
const form = $( window.eddStripe.cardElement._parent ).closest( 'form' ),
|
||||
tokenInput = $( '#edd-process-stripe-token' );
|
||||
|
||||
return apiRequest( 'edds_update_intent', {
|
||||
intent_id: intent.id,
|
||||
intent_type: intent.object,
|
||||
timestamp: tokenInput.length ? tokenInput.data( 'timestamp' ) : '',
|
||||
token: tokenInput.length ? tokenInput.data( 'token' ) : '',
|
||||
form_data: form.serialize(),
|
||||
...data,
|
||||
} )
|
||||
// Returns just the PaymentIntent object for easier reprocessing.
|
||||
.then( function( response ) {
|
||||
return response.intent;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the PaymentIntent requires further action.
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js/reference
|
||||
*
|
||||
* @param {Object} intent Stripe PaymentIntent or SetupIntent.
|
||||
* @param {Object} data Extra data to pass to the intent action.
|
||||
*/
|
||||
export async function handle( intent, data ) {
|
||||
// requires_confirmation
|
||||
if ( 'requires_confirmation' === intent.status ) {
|
||||
// Attempt to capture.
|
||||
const confirmedIntent = await confirm( intent );
|
||||
|
||||
// Run through again.
|
||||
return await handle( confirmedIntent );
|
||||
}
|
||||
|
||||
// requires_payment_method
|
||||
// @link https://stripe.com/docs/payments/intents#intent-statuses
|
||||
if (
|
||||
'requires_payment_method' === intent.status ||
|
||||
'requires_source' === intent.status
|
||||
) {
|
||||
// Attempt to update.
|
||||
const updatedIntent = await update( intent, data );
|
||||
|
||||
// Run through again.
|
||||
return await handle( updatedIntent, data );
|
||||
}
|
||||
|
||||
// requires_action
|
||||
// @link https://stripe.com/docs/payments/intents#intent-statuses
|
||||
if (
|
||||
( 'requires_action' === intent.status && 'use_stripe_sdk' === intent.next_action.type ) ||
|
||||
( 'requires_source_action' === intent.status && 'use_stripe_sdk' === intent.next_action.type )
|
||||
) {
|
||||
let cardHandler = 'setup_intent' === intent.object ? 'handleCardSetup' : 'handleCardAction';
|
||||
|
||||
if ( 'automatic' === intent.confirmation_method ) {
|
||||
cardHandler = 'handleCardPayment';
|
||||
}
|
||||
|
||||
return window.eddStripe[ cardHandler ]( intent.client_secret )
|
||||
.then( async ( result ) => {
|
||||
if ( result.error ) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
const {
|
||||
setupIntent,
|
||||
paymentIntent,
|
||||
} = result;
|
||||
|
||||
// Run through again.
|
||||
return await handle( setupIntent || paymentIntent );
|
||||
} );
|
||||
}
|
||||
|
||||
// Nothing done, return Intent.
|
||||
return intent;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/* global $, edd_scripts, ajaxurl */
|
||||
|
||||
/**
|
||||
* Sends an API request to admin-ajax.php
|
||||
*
|
||||
* @link https://github.com/WordPress/WordPress/blob/master/wp-includes/js/wp-util.js#L49
|
||||
*
|
||||
* @param {string} action AJAX action to send to admin-ajax.php
|
||||
* @param {Object} data Additional data to send to the action.
|
||||
* @return {Promise} jQuery Promise.
|
||||
*/
|
||||
export function apiRequest( action, data ) {
|
||||
const options = {
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
xhrFields: {
|
||||
withCredentials: true,
|
||||
},
|
||||
url: ( window.edd_scripts && window.edd_scripts.ajaxurl ) || window.ajaxurl,
|
||||
data: {
|
||||
action,
|
||||
...data,
|
||||
},
|
||||
};
|
||||
|
||||
const deferred = $.Deferred( function( deferred ) {
|
||||
// Use with PHP's wp_send_json_success() and wp_send_json_error()
|
||||
deferred.jqXHR = $.ajax( options ).done( function( response ) {
|
||||
// Treat a response of 1 or 'success' as successful for backward compatibility with existing handlers.
|
||||
if ( response === '1' || response === 1 ) {
|
||||
response = { success: true };
|
||||
}
|
||||
|
||||
if ( typeof response === 'object' && typeof response.success !== undefined ) {
|
||||
deferred[ response.success ? 'resolveWith' : 'rejectWith' ]( this, [ response.data ] );
|
||||
} else {
|
||||
deferred.rejectWith( this, [ response ] );
|
||||
}
|
||||
} ).fail( function() {
|
||||
deferred.rejectWith( this, arguments );
|
||||
} );
|
||||
} );
|
||||
|
||||
const promise = deferred.promise();
|
||||
promise.abort = function() {
|
||||
deferred.jqXHR.abort();
|
||||
return this;
|
||||
};
|
||||
|
||||
return promise;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
import { forEach } from 'utils'; // eslint-disable-line @wordpress/dependency-group
|
||||
|
||||
/**
|
||||
* forEach implementation that can handle anything.
|
||||
*/
|
||||
export { default as forEach } from 'lodash.foreach';
|
||||
|
||||
/**
|
||||
* DOM ready.
|
||||
*
|
||||
* Handles multiple callbacks.
|
||||
*
|
||||
* @param {Function} Callback function to run.
|
||||
*/
|
||||
export function domReady() {
|
||||
forEach( arguments, ( callback ) => {
|
||||
document.addEventListener( 'DOMContentLoaded', callback );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all following siblings of an element.
|
||||
*
|
||||
* @param {HTMLElement} el Starting element.
|
||||
* @return {Array} siblings List of sibling elements.
|
||||
*/
|
||||
export function getNextSiblings( el ) {
|
||||
const siblings = [];
|
||||
let sibling = el.nextElementSibling;
|
||||
|
||||
while ( sibling ) {
|
||||
if ( sibling.nodeType === 1 ) {
|
||||
siblings.push( sibling );
|
||||
}
|
||||
|
||||
sibling = sibling.nextElementSibling;
|
||||
}
|
||||
|
||||
return siblings;
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { forEach } from 'utils';
|
||||
|
||||
/**
|
||||
* Checks is a form passes HTML5 validation.
|
||||
*
|
||||
* @param {HTMLElement} form Form to trigger validation on.
|
||||
* @return {Bool} If the form has valid inputs.
|
||||
*/
|
||||
export function hasValidInputs( form ) {
|
||||
let plainInputsValid = true;
|
||||
|
||||
forEach( form.querySelectorAll( 'input' ), function( input ) {
|
||||
if ( input.checkValidity && ! input.checkValidity() ) {
|
||||
plainInputsValid = false;
|
||||
}
|
||||
} );
|
||||
|
||||
return plainInputsValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers HTML5 browser validation.
|
||||
*
|
||||
* @param {HTMLElement} form Form to trigger validation on.
|
||||
*/
|
||||
export function triggerBrowserValidation( form ) {
|
||||
const submit = document.createElement( 'input' );
|
||||
submit.type = 'submit';
|
||||
submit.style.display = 'none';
|
||||
|
||||
form.appendChild( submit );
|
||||
submit.click();
|
||||
submit.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input's value, or null.
|
||||
*
|
||||
* @param {HTMLElement} field Field to retrieve value from.
|
||||
* @return {null|string} Value if the field has a value.
|
||||
*/
|
||||
export function fieldValueOrNull( field ) {
|
||||
if ( ! field ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( '' === field.value ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return field.value;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import './polyfill-includes.js';
|
||||
import './polyfill-closest.js';
|
||||
import './polyfill-object-entries.js';
|
||||
import './polyfill-remove.js';
|
||||
|
||||
export * from './api-request.js';
|
||||
export * from './dom.js';
|
||||
export * from './notice.js';
|
||||
export * from './form.js';
|
@ -0,0 +1,61 @@
|
||||
/* global $, edd_stripe_vars */
|
||||
|
||||
/**
|
||||
* Generates a notice element.
|
||||
*
|
||||
* @param {string} message The notice text.
|
||||
* @param {string} type The type of notice. error or success. Default error.
|
||||
* @return {Element} HTML element containing errors.
|
||||
*/
|
||||
export function generateNotice( message, type = 'error' ) {
|
||||
const notice = document.createElement( 'p' );
|
||||
notice.classList.add( 'edd-alert' );
|
||||
notice.classList.add( 'edd-stripe-alert' );
|
||||
notice.style.clear = 'both';
|
||||
|
||||
if ( 'error' === type ) {
|
||||
notice.classList.add( 'edd-alert-error' );
|
||||
} else {
|
||||
notice.classList.add( 'edd-alert-success' );
|
||||
}
|
||||
|
||||
notice.innerHTML = message || edd_stripe_vars.generic_error;
|
||||
|
||||
return notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a notice.
|
||||
*
|
||||
*
|
||||
* @param {object} args Output arguments.
|
||||
* @param {string} args.errorType The type of notice. error or success
|
||||
* @param {string} args.errorMessasge The notice text.
|
||||
* @param {HTMLElement} args.errorContainer HTML element containing errors.
|
||||
* @param {bool} args.errorContainerReplace If true Appends the notice before
|
||||
* the container.
|
||||
*/
|
||||
export function outputNotice( {
|
||||
errorType,
|
||||
errorMessage,
|
||||
errorContainer,
|
||||
errorContainerReplace = true,
|
||||
} ) {
|
||||
const $errorContainer = $( errorContainer );
|
||||
const notice = generateNotice( errorMessage, errorType );
|
||||
|
||||
if ( true === errorContainerReplace ) {
|
||||
$errorContainer.html( notice );
|
||||
} else {
|
||||
$errorContainer.before( notice );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a notice.
|
||||
*
|
||||
* @param {HTMLElement} errorContainer HTML element containing errors.
|
||||
*/
|
||||
export function clearNotice( errorContainer ) {
|
||||
$( errorContainer ).html( '' );
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/// Polyfill .closest
|
||||
// @link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
|
||||
if ( ! Element.prototype.matches ) {
|
||||
Element.prototype.matches =
|
||||
Element.prototype.msMatchesSelector ||
|
||||
Element.prototype.webkitMatchesSelector;
|
||||
}
|
||||
|
||||
if ( ! Element.prototype.closest ) {
|
||||
Element.prototype.closest = function( s ) {
|
||||
let el = this;
|
||||
|
||||
do {
|
||||
if ( Element.prototype.matches.call( el, s ) ) return el;
|
||||
|
||||
el = el.parentElement || el.parentNode;
|
||||
} while ( el !== null && el.nodeType === 1 );
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// Polyfill string.contains
|
||||
// @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill
|
||||
if ( ! String.prototype.includes ) {
|
||||
String.prototype.includes = function( search, start ) {
|
||||
'use strict';
|
||||
|
||||
if ( typeof start !== 'number' ) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if ( start + search.length > this.length ) {
|
||||
return false;
|
||||
} else {
|
||||
return this.indexOf( search, start ) !== -1;
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
/// Polyfill Object.entries
|
||||
// @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill
|
||||
if ( ! Object.entries ) {
|
||||
Object.entries = function( obj ) {
|
||||
var ownProps = Object.keys( obj ),
|
||||
i = ownProps.length,
|
||||
resArray = new Array( i ); // preallocate the Array
|
||||
|
||||
while ( i-- ) {
|
||||
resArray[ i ] = [ ownProps[ i ], obj[ ownProps[ i ] ] ];
|
||||
}
|
||||
|
||||
return resArray;
|
||||
};
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/// Polyfill .remove
|
||||
// @link https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove#Polyfill
|
||||
( function ( arr ) {
|
||||
arr.forEach( function( item ) {
|
||||
if ( item.hasOwnProperty( 'remove' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.defineProperty( item, 'remove', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function remove() {
|
||||
this.parentNode.removeChild( this );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
} )( [ Element.prototype, CharacterData.prototype, DocumentType.prototype ] );
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Easy Digital Downloads - Stripe Pro Payment Gateway
|
||||
* Plugin URI: https://easydigitaldownloads.com/downloads/stripe-gateway/
|
||||
* Description: Adds a payment gateway for Stripe.com
|
||||
* Version: 2.8.13
|
||||
* Requires at least: 4.4
|
||||
* Requires PHP: 5.6
|
||||
* Author: Easy Digital Downloads
|
||||
* Author URI: https://easydigitaldownloads.com
|
||||
* Text Domain: edds
|
||||
* Domain Path: languages
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the one true instance of EDD_Stripe
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @return void|\EDD_Stripe EDD_Stripe instance or void if Easy Digital
|
||||
* Downloads is not active.
|
||||
*/
|
||||
function edd_stripe_core_bootstrap() {
|
||||
// Easy Digital Downloads is not active, do nothing.
|
||||
if ( ! function_exists( 'EDD' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stripe is already active, do nothing.
|
||||
if ( class_exists( 'EDD_Stripe' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! defined( 'EDDS_PLUGIN_DIR' ) ) {
|
||||
define( 'EDDS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
}
|
||||
|
||||
if ( ! defined( 'EDDSTRIPE_PLUGIN_URL' ) ) {
|
||||
define( 'EDDSTRIPE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||
}
|
||||
|
||||
if ( ! defined( 'EDD_STRIPE_PLUGIN_FILE' ) ) {
|
||||
define( 'EDD_STRIPE_PLUGIN_FILE', __FILE__ );
|
||||
}
|
||||
|
||||
if ( ! defined( 'EDD_STRIPE_VERSION' ) ) {
|
||||
define( 'EDD_STRIPE_VERSION', '2.8.13' );
|
||||
}
|
||||
|
||||
if ( ! defined( 'EDD_STRIPE_API_VERSION' ) ) {
|
||||
define( 'EDD_STRIPE_API_VERSION', '2020-03-02' );
|
||||
}
|
||||
|
||||
if ( ! defined( 'EDD_STRIPE_PARTNER_ID' ) ) {
|
||||
define( 'EDD_STRIPE_PARTNER_ID', 'pp_partner_DKh7NDe3Y5G8XG' );
|
||||
}
|
||||
|
||||
include_once __DIR__ . '/includes/class-edd-stripe.php';
|
||||
|
||||
// Initial instantiation.
|
||||
EDD_Stripe::instance();
|
||||
}
|
||||
add_action( 'plugins_loaded', 'edd_stripe_core_bootstrap' );
|
||||
|
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Trigger preapproved payment charge
|
||||
*
|
||||
* @since 1.6
|
||||
* @return void
|
||||
*/
|
||||
function edds_process_preapproved_charge() {
|
||||
|
||||
if( empty( $_GET['nonce'] ) )
|
||||
return;
|
||||
|
||||
if( ! wp_verify_nonce( $_GET['nonce'], 'edds-process-preapproval' ) )
|
||||
return;
|
||||
|
||||
$payment_id = absint( $_GET['payment_id'] );
|
||||
$charge = edds_charge_preapproved( $payment_id );
|
||||
|
||||
if ( $charge ) {
|
||||
wp_redirect( esc_url_raw( add_query_arg( array( 'edd-message' => 'preapproval-charged' ), admin_url( 'edit.php?post_type=download&page=edd-payment-history' ) ) ) ); exit;
|
||||
} else {
|
||||
wp_redirect( esc_url_raw( add_query_arg( array( 'edd-message' => 'preapproval-failed' ), admin_url( 'edit.php?post_type=download&page=edd-payment-history' ) ) ) ); exit;
|
||||
}
|
||||
|
||||
}
|
||||
add_action( 'edd_charge_stripe_preapproval', 'edds_process_preapproved_charge' );
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a preapproved payment
|
||||
*
|
||||
* @since 1.6
|
||||
* @return void
|
||||
*/
|
||||
function edds_process_preapproved_cancel() {
|
||||
global $edd_options;
|
||||
|
||||
if( empty( $_GET['nonce'] ) )
|
||||
return;
|
||||
|
||||
if( ! wp_verify_nonce( $_GET['nonce'], 'edds-process-preapproval' ) )
|
||||
return;
|
||||
|
||||
$payment_id = absint( $_GET['payment_id'] );
|
||||
|
||||
if ( empty( $payment_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment = edd_get_payment( $payment_id );
|
||||
$customer_id = $payment->get_meta( '_edds_stripe_customer_id', true );
|
||||
$status = $payment->status;
|
||||
|
||||
if ( empty( $customer_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'preapproval' !== $status ) {
|
||||
return;
|
||||
}
|
||||
|
||||
edd_insert_payment_note( $payment_id, __( 'Preapproval cancelled', 'easy-digital-downloads' ) );
|
||||
edd_update_payment_status( $payment_id, 'cancelled' );
|
||||
$payment->delete_meta( '_edds_stripe_customer_id' );
|
||||
|
||||
wp_redirect( esc_url_raw( add_query_arg( array( 'edd-message' => 'preapproval-cancelled' ), admin_url( 'edit.php?post_type=download&page=edd-payment-history' ) ) ) ); exit;
|
||||
}
|
||||
add_action( 'edd_cancel_stripe_preapproval', 'edds_process_preapproved_cancel' );
|
||||
|
||||
/**
|
||||
* Adds a JS confirmation to check whether a preapproved payment should really be cancelled.
|
||||
*
|
||||
* @since 2.8.10
|
||||
* @return void
|
||||
*/
|
||||
add_action( 'admin_print_footer_scripts-download_page_edd-payment-history', function () {
|
||||
?>
|
||||
<script>
|
||||
document.addEventListener( 'DOMContentLoaded', function() {
|
||||
var cancelLinks = document.querySelectorAll( '.row-actions .cancel-preapproval a' );
|
||||
cancelLinks.forEach( function( link ) {
|
||||
link.addEventListener( 'click', function( e ) {
|
||||
if ( ! confirm( '<?php esc_attr_e( 'Are you sure you want to cancel this order?', 'easy-digital-downloads' ); ?>' ) ) {
|
||||
e.preventDefault();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
</script>
|
||||
<?php
|
||||
} );
|
||||
|
||||
/**
|
||||
* Admin Messages
|
||||
*
|
||||
* @since 1.6
|
||||
* @return void
|
||||
*/
|
||||
function edds_admin_messages() {
|
||||
|
||||
if ( isset( $_GET['edd-message'] ) && 'preapproval-charged' == $_GET['edd-message'] ) {
|
||||
add_settings_error( 'edds-notices', 'edds-preapproval-charged', __( 'The preapproved payment was successfully charged.', 'easy-digital-downloads' ), 'updated' );
|
||||
}
|
||||
if ( isset( $_GET['edd-message'] ) && 'preapproval-failed' == $_GET['edd-message'] ) {
|
||||
add_settings_error( 'edds-notices', 'edds-preapproval-charged', __( 'The preapproved payment failed to be charged. View order details for further details.', 'easy-digital-downloads' ), 'error' );
|
||||
}
|
||||
if ( isset( $_GET['edd-message'] ) && 'preapproval-cancelled' == $_GET['edd-message'] ) {
|
||||
add_settings_error( 'edds-notices', 'edds-preapproval-cancelled', __( 'The preapproved payment was successfully cancelled.', 'easy-digital-downloads' ), 'updated' );
|
||||
}
|
||||
|
||||
if( isset( $_GET['edd_gateway_connect_error'], $_GET['edd-message'] ) ) {
|
||||
/* translators: %1$s Stripe Connect error message. %2$s Retry URL. */
|
||||
echo '<div class="notice notice-error"><p>' . sprintf( __( 'There was an error connecting your Stripe account. Message: %1$s. Please <a href="%2$s">try again</a>.', 'easy-digital-downloads' ), esc_html( urldecode( $_GET['edd-message'] ) ), esc_url( admin_url( 'edit.php?post_type=download&page=edd-settings&tab=gateways§ion=edd-stripe' ) ) ) . '</p></div>';
|
||||
add_filter( 'wp_parse_str', function( $ar ) {
|
||||
if( isset( $ar['edd_gateway_connect_error'] ) ) {
|
||||
unset( $ar['edd_gateway_connect_error'] );
|
||||
}
|
||||
|
||||
if( isset( $ar['edd-message'] ) ) {
|
||||
unset( $ar['edd-message'] );
|
||||
}
|
||||
return $ar;
|
||||
});
|
||||
}
|
||||
|
||||
settings_errors( 'edds-notices' );
|
||||
}
|
||||
add_action( 'admin_notices', 'edds_admin_messages' );
|
||||
|
||||
/**
|
||||
* Add payment meta item to payments that used an existing card
|
||||
*
|
||||
* @since 2.6
|
||||
* @param $payment_id
|
||||
* @return void
|
||||
*/
|
||||
function edds_show_existing_card_meta( $payment_id ) {
|
||||
$payment = new EDD_Payment( $payment_id );
|
||||
$existing_card = $payment->get_meta( '_edds_used_existing_card' );
|
||||
if ( ! empty( $existing_card ) ) {
|
||||
?>
|
||||
<div class="edd-order-stripe-existing-card edd-admin-box-inside">
|
||||
<p>
|
||||
<span class="label"><?php _e( 'Used Existing Card:', 'easy-digital-downloads' ); ?></span>
|
||||
<span><?php _e( 'Yes', 'easy-digital-downloads' ); ?></span>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
add_action( 'edd_view_order_details_payment_meta_after', 'edds_show_existing_card_meta', 10, 1 );
|
||||
|
||||
/**
|
||||
* Handles redirects to the Stripe settings page under certain conditions.
|
||||
*
|
||||
* @since 2.6.14
|
||||
*/
|
||||
function edds_stripe_connect_test_mode_toggle_redirect() {
|
||||
|
||||
// Check for our marker
|
||||
if( ! isset( $_POST['edd-test-mode-toggled'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( ! current_user_can( 'manage_shop_settings' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( false === edds_is_gateway_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the redirect that happens when options are saved and
|
||||
* add query args to redirect to the Stripe settings page
|
||||
* and to show a notice about connecting with Stripe.
|
||||
*/
|
||||
add_filter( 'wp_redirect', function( $location ) {
|
||||
if( false !== strpos( $location, 'page=edd-settings' ) && false !== strpos( $location, 'settings-updated=true' ) ) {
|
||||
$location = add_query_arg(
|
||||
array(
|
||||
'edd-message' => 'connect-to-stripe',
|
||||
),
|
||||
$location
|
||||
);
|
||||
}
|
||||
return $location;
|
||||
} );
|
||||
|
||||
}
|
||||
add_action( 'admin_init', 'edds_stripe_connect_test_mode_toggle_redirect' );
|
||||
|
||||
/**
|
||||
* Adds a "Refund Charge in Stripe" checkbox to the refund UI.
|
||||
*
|
||||
* @param \EDD\Orders\Order $order
|
||||
*
|
||||
* @since 2.8.7
|
||||
*/
|
||||
function edds_show_refund_checkbox( \EDD\Orders\Order $order ) {
|
||||
if ( 'stripe' !== $order->gateway ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="edd-form-group edd-stripe-refund-transaction">
|
||||
<div class="edd-form-group__control">
|
||||
<input type="checkbox" id="edd-stripe-refund" name="edd-stripe-refund" class="edd-form-group__input" value="1">
|
||||
<label for="edd-stripe-refund" class="edd-form-group__label">
|
||||
<?php esc_html_e( 'Refund Charge in Stripe', 'easy-digital-downloads' ); ?>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
add_action( 'edd_after_submit_refund_table', 'edds_show_refund_checkbox' );
|
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Given a Payment ID, extract the transaction ID from Stripe
|
||||
*
|
||||
* @param string $payment_id Payment ID
|
||||
* @return string Transaction ID
|
||||
*/
|
||||
function edds_get_payment_transaction_id( $payment_id ) {
|
||||
|
||||
$txn_id = '';
|
||||
$notes = edd_get_payment_notes( $payment_id );
|
||||
|
||||
foreach ( $notes as $note ) {
|
||||
if ( preg_match( '/^Stripe Charge ID: ([^\s]+)/', $note->comment_content, $match ) ) {
|
||||
$txn_id = $match[1];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'edds_set_payment_transaction_id', $txn_id, $payment_id );
|
||||
}
|
||||
add_filter( 'edd_get_payment_transaction_id-stripe', 'edds_get_payment_transaction_id', 10, 1 );
|
||||
|
||||
/**
|
||||
* Given a transaction ID, generate a link to the Stripe transaction ID details
|
||||
*
|
||||
* @since 1.9.1
|
||||
* @param string $transaction_id The Transaction ID
|
||||
* @param int $payment_id The payment ID for this transaction
|
||||
* @return string A link to the Stripe transaction details
|
||||
*/
|
||||
function edd_stripe_link_transaction_id( $transaction_id, $payment_id ) {
|
||||
|
||||
$test = edd_get_payment_meta( $payment_id, '_edd_payment_mode' ) === 'test' ? 'test/' : '';
|
||||
$status = edd_get_payment_status( $payment_id );
|
||||
|
||||
if ( 'preapproval' === $status ) {
|
||||
$url = '<a href="https://dashboard.stripe.com/' . esc_attr( $test ) . 'setup_intents/' . esc_attr( $transaction_id ) . '" target="_blank">' . esc_html( $transaction_id ) . '</a>';
|
||||
} else {
|
||||
$url = '<a href="https://dashboard.stripe.com/' . esc_attr( $test ) . 'payments/' . esc_attr( $transaction_id ) . '" target="_blank">' . esc_html( $transaction_id ) . '</a>';
|
||||
}
|
||||
return apply_filters( 'edd_stripe_link_payment_details_transaction_id', $url );
|
||||
|
||||
}
|
||||
add_filter( 'edd_payment_details_transaction_id-stripe', 'edd_stripe_link_transaction_id', 10, 2 );
|
||||
|
||||
/**
|
||||
* Show the Process / Cancel buttons for preapproved payments
|
||||
*
|
||||
* @since 1.6
|
||||
* @return string
|
||||
*/
|
||||
function edds_payments_column_data( $value, $payment_id, $column_name ) {
|
||||
if ( 'status' !== $column_name ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$status = edd_get_payment_status( $payment_id );
|
||||
if ( ! in_array( $status, array( 'preapproval', 'preapproval_pending' ), true ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( function_exists( 'edd_get_order_meta' ) ) {
|
||||
$customer_id = edd_get_order_meta( $payment_id, '_edds_stripe_customer_id', true );
|
||||
} else {
|
||||
$customer_id = edd_get_payment_meta( $payment_id, '_edds_stripe_customer_id', true );
|
||||
}
|
||||
|
||||
if ( empty( $customer_id ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$nonce = wp_create_nonce( 'edds-process-preapproval' );
|
||||
|
||||
$base_args = array(
|
||||
'post_type' => 'download',
|
||||
'page' => 'edd-payment-history',
|
||||
'payment_id' => urlencode( $payment_id ),
|
||||
'nonce' => urlencode( $nonce ),
|
||||
);
|
||||
$preapproval_args = array(
|
||||
'edd-action' => 'charge_stripe_preapproval',
|
||||
);
|
||||
$cancel_args = array(
|
||||
'preapproval_key' => urlencode( $customer_id ),
|
||||
'edd-action' => 'cancel_stripe_preapproval',
|
||||
);
|
||||
|
||||
$actions = array(
|
||||
sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url(
|
||||
add_query_arg(
|
||||
array_merge( $base_args, $preapproval_args ),
|
||||
admin_url( 'edit.php' )
|
||||
)
|
||||
),
|
||||
esc_html__( 'Process', 'easy-digital-downloads' )
|
||||
),
|
||||
sprintf(
|
||||
'<span class="cancel-preapproval"><a href="%s">%s</a></span>',
|
||||
esc_url(
|
||||
add_query_arg(
|
||||
array_merge( $base_args, $cancel_args ),
|
||||
admin_url( 'edit.php' )
|
||||
)
|
||||
),
|
||||
esc_html__( 'Cancel', 'easy-digital-downloads' )
|
||||
),
|
||||
);
|
||||
|
||||
$value .= '<p class="row-actions">';
|
||||
$value .= implode( ' | ', $actions );
|
||||
$value .= '</p>';
|
||||
|
||||
return $value;
|
||||
}
|
||||
add_filter( 'edd_payments_table_column', 'edds_payments_column_data', 20, 3 );
|
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* Notices registry.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6.19
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements a registry for notices.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
class EDD_Stripe_Admin_Notices_Registry extends EDD_Stripe_Utils_Registry implements EDD_Stripe_Utils_Static_Registry {
|
||||
|
||||
/**
|
||||
* Item error label.
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @var string
|
||||
*/
|
||||
public static $item_error_label = 'admin notice';
|
||||
|
||||
/**
|
||||
* The one true Notices_Registry instance.
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @var EDD_Stripe_Notices_Registry
|
||||
*/
|
||||
public static $instance;
|
||||
|
||||
/**
|
||||
* Retrieves the one true Admin Notices registry instance.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return EDD_Stripe_Admin_Notices_Registry Report registry instance.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new EDD_Stripe_Admin_Notices_Registry();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the notices registry.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
public function init() {
|
||||
/**
|
||||
* Fires during instantiation of the notices registry.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param EDD_Stripe_Notices_Registry $this Registry instance.
|
||||
*/
|
||||
do_action( 'edds_admin_notices_registry_init', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new notice.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @param string $notice_id Unique notice ID.
|
||||
* @param array $notice_args {
|
||||
* Arguments for adding a new notice.
|
||||
*
|
||||
* @type string|callable $message Notice message or a callback to retrieve it.
|
||||
* @type string $type Notice type. Accepts 'success', 'info', 'warning', 'error'.
|
||||
* Default 'success'.
|
||||
* @type bool $dismissible Detrmines if the notice can be hidden for the current install.
|
||||
* Default true
|
||||
* }
|
||||
* @return true
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add( $notice_id, $notice_args ) {
|
||||
$defaults = array(
|
||||
'type' => 'success',
|
||||
'dismissible' => true,
|
||||
);
|
||||
|
||||
$notice_args = array_merge( $defaults, $notice_args );
|
||||
|
||||
if ( empty( $notice_args['message'] ) ) {
|
||||
throw new Exception( esc_html__( 'A message must be specified for each notice.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
if ( ! in_array( $notice_args['type'], array( 'success', 'info', 'warning', 'error' ), true ) ) {
|
||||
$notice_args['type'] = 'success';
|
||||
}
|
||||
|
||||
return $this->add_item( $notice_id, $notice_args );
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/**
|
||||
* Manage the notices registry.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6.19
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements logic for displaying notifications.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
class EDD_Stripe_Admin_Notices {
|
||||
|
||||
/**
|
||||
* Registry.
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @var EDD_Stripe_Notices_Registry
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* EDD_Stripe_Admin_Notices
|
||||
*
|
||||
* @param EDD_Stripe_Notices_Registry $registry Notices registry.
|
||||
*/
|
||||
public function __construct( $registry ) {
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the option to manage the status of the notice.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $notice_id ID of the notice to generate the name with.
|
||||
* @return string
|
||||
*/
|
||||
public function get_dismissed_option_name( $notice_id ) {
|
||||
// Ensures backwards compatibility for notices dismissed before 2.6.19
|
||||
switch ( $notice_id ) {
|
||||
case 'stripe-connect':
|
||||
$option_name = 'edds_stripe_connect_intro_notice_dismissed';
|
||||
break;
|
||||
default:
|
||||
$option_name = sprintf( 'edds_notice_%1$s_dismissed', $notice_id );
|
||||
}
|
||||
|
||||
return $option_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses a notice.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $notice_id ID of the notice to dismiss.
|
||||
* @return bool True if notice is successfully dismissed. False on failure.
|
||||
*/
|
||||
public function dismiss( $notice_id ) {
|
||||
return update_option( $this->get_dismissed_option_name( $notice_id ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a notice.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $notice_id ID of the notice to restore.
|
||||
* @return bool True if notice is successfully restored. False on failure.
|
||||
*/
|
||||
public function restore( $notice_id ) {
|
||||
return delete_option( $this->get_dismissed_option_name( $notice_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a notice has been permanently dismissed.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param int $notice_id Notice ID.
|
||||
* @return bool True if the notice is dismissed.
|
||||
*/
|
||||
public function is_dismissed( $notice_id ) {
|
||||
return (bool) get_option( $this->get_dismissed_option_name( $notice_id ), false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a given notice's output.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $notice_id ID of the notice to build.
|
||||
*/
|
||||
public function build( $notice_id ) {
|
||||
$output = '';
|
||||
$notice = $this->registry->get_item( $notice_id );
|
||||
|
||||
if ( empty( $notice ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
if ( true === $this->is_dismissed( $notice_id ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
if ( is_callable( $notice['message'] ) ) {
|
||||
$message = call_user_func( $notice['message'] );
|
||||
} else {
|
||||
$message = $notice['message'];
|
||||
}
|
||||
|
||||
$classes = array(
|
||||
'edds-admin-notice',
|
||||
'notice',
|
||||
'notice-' . $notice['type'],
|
||||
);
|
||||
|
||||
if ( $notice['dismissible'] ) {
|
||||
$classes[] = 'is-dismissible';
|
||||
}
|
||||
|
||||
$output = sprintf(
|
||||
'<div id="edds-%1$s-notice" class="%2$s" data-id="%1$s" data-nonce="%3$s" role="alert">%4$s</div>',
|
||||
esc_attr( $notice_id ),
|
||||
esc_attr( implode( ' ', $classes ) ),
|
||||
wp_create_nonce( "edds-dismiss-{$notice_id}-nonce" ),
|
||||
$message
|
||||
);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a given notice.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
public function output( $notice_id ) {
|
||||
echo $this->build( $notice_id );
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* Bootstraps and outputs notices.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6.19
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers scripts to manage dismissing notices.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
function edds_admin_notices_scripts() {
|
||||
wp_register_script(
|
||||
'edds-admin-notices',
|
||||
EDDSTRIPE_PLUGIN_URL . 'assets/js/build/notices.min.js',
|
||||
array(
|
||||
'wp-util',
|
||||
'jquery',
|
||||
),
|
||||
EDD_STRIPE_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
add_action( 'admin_enqueue_scripts', 'edds_admin_notices_scripts' );
|
||||
|
||||
/**
|
||||
* Registers admin notices.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return true|WP_Error True if all notices are registered, otherwise WP_Error.
|
||||
*/
|
||||
function edds_admin_notices_register() {
|
||||
$registry = edds_get_registry( 'admin-notices' );
|
||||
|
||||
if ( ! $registry ) {
|
||||
return new WP_Error( 'edds-invalid-registry', esc_html__( 'Unable to locate registry', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
try {
|
||||
// PHP
|
||||
$registry->add(
|
||||
'php-requirement',
|
||||
array(
|
||||
'message' => function() {
|
||||
ob_start();
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/notices/php-requirement.php';
|
||||
return ob_get_clean();
|
||||
},
|
||||
'type' => 'error',
|
||||
'dismissible' => false,
|
||||
)
|
||||
);
|
||||
|
||||
// EDD 2.11
|
||||
$registry->add(
|
||||
'edd-requirement',
|
||||
array(
|
||||
'message' => function() {
|
||||
ob_start();
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/notices/edd-requirement.php';
|
||||
return ob_get_clean();
|
||||
},
|
||||
'type' => 'error',
|
||||
'dismissible' => false,
|
||||
)
|
||||
);
|
||||
|
||||
// Recurring requirement.
|
||||
$registry->add(
|
||||
'edd-recurring-requirement',
|
||||
array(
|
||||
'message' => function() {
|
||||
ob_start();
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/notices/edd-recurring-requirement.php';
|
||||
return ob_get_clean();
|
||||
},
|
||||
'type' => 'error',
|
||||
'dismissible' => false,
|
||||
)
|
||||
);
|
||||
|
||||
// Enable gateway.
|
||||
$registry->add(
|
||||
'edd-stripe-core',
|
||||
array(
|
||||
'message' => function() {
|
||||
ob_start();
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/notices/edd-stripe-core.php';
|
||||
return ob_get_clean();
|
||||
},
|
||||
'type' => 'info',
|
||||
'dismissible' => true,
|
||||
)
|
||||
);
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error(
|
||||
'edds-invalid-notices-registration',
|
||||
esc_html( $e->getMessage() )
|
||||
);
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
add_action( 'admin_init', 'edds_admin_notices_register' );
|
||||
|
||||
/**
|
||||
* Conditionally prints registered notices.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
function edds_admin_notices_print() {
|
||||
// Current user needs capability to dismiss notices.
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$registry = edds_get_registry( 'admin-notices' );
|
||||
|
||||
if ( ! $registry ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = new EDD_Stripe_Admin_Notices( $registry );
|
||||
|
||||
wp_enqueue_script( 'edds-admin-notices' );
|
||||
|
||||
try {
|
||||
// PHP 5.6 requirement.
|
||||
if (
|
||||
false === edds_has_met_requirements( 'php' ) &&
|
||||
true === edds_is_pro()
|
||||
) {
|
||||
$notices->output( 'php-requirement' );
|
||||
}
|
||||
|
||||
// EDD 2.9 requirement.
|
||||
if ( false === edds_has_met_requirements( 'edd' ) ) {
|
||||
$notices->output( 'edd-requirement' );
|
||||
}
|
||||
|
||||
// Recurring 2.10.0 requirement.
|
||||
if ( false === edds_has_met_requirements( 'recurring' ) ) {
|
||||
$notices->output( 'edd-recurring-requirement' );
|
||||
}
|
||||
|
||||
// Stripe in Core notice.
|
||||
if ( false === edds_is_pro() && false === edd_is_gateway_active( 'stripe' ) ) {
|
||||
$notices->output( 'edd-stripe-core' );
|
||||
}
|
||||
} catch( Exception $e ) {}
|
||||
}
|
||||
add_action( 'admin_notices', 'edds_admin_notices_print' );
|
||||
|
||||
/**
|
||||
* Handles AJAX dismissal of notices.
|
||||
*
|
||||
* WordPress automatically removes the notices, so the response here is arbitrary.
|
||||
* If the notice cannot be dismissed it will simply reappear when the page is refreshed.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
function edds_admin_notices_dismiss_ajax() {
|
||||
$notice_id = isset( $_REQUEST[ 'id' ] ) ? esc_attr( $_REQUEST['id'] ) : false;
|
||||
$nonce = isset( $_REQUEST[ 'nonce' ] ) ? esc_attr( $_REQUEST['nonce'] ) : false;
|
||||
|
||||
if ( ! ( $notice_id && $nonce ) ) {
|
||||
return wp_send_json_error();
|
||||
}
|
||||
|
||||
if ( ! wp_verify_nonce( $nonce, "edds-dismiss-{$notice_id}-nonce" ) ) {
|
||||
return wp_send_json_error();
|
||||
}
|
||||
|
||||
$registry = edds_get_registry( 'admin-notices' );
|
||||
|
||||
if ( ! $registry ) {
|
||||
return wp_send_json_error();
|
||||
}
|
||||
|
||||
$notices = new EDD_Stripe_Admin_Notices( $registry );
|
||||
$dismissed = $notices->dismiss( $notice_id );
|
||||
|
||||
if ( true === $dismissed ) {
|
||||
return wp_send_json_success();
|
||||
} else {
|
||||
return wp_send_json_error();
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_admin_notices_dismiss_ajax', 'edds_admin_notices_dismiss_ajax' );
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Notice: edd-recurring-requirement
|
||||
*
|
||||
* @package EDD_Stripe\Admin\Notices
|
||||
* @copyright Copyright (c) 2021, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.1
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'Credit card payments with Stripe are currently disabled.', 'easy-digital-downloads' ); ?></strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening strong tag, do not translate. %2$s Closing strong tag, do not translate. %3$s Opening code tag, do not translate. %4$s Closing code tag, do not translate. */
|
||||
__( 'To continue accepting credit card payments with Stripe please update %1$sEasy Digital Downloads - Recurring Payments%2$s to version %3$s2.10%4$s or higher.', 'easy-digital-downloads' ),
|
||||
'<strong>',
|
||||
'</strong>',
|
||||
'<code>',
|
||||
'</code>'
|
||||
),
|
||||
array(
|
||||
'code' => true,
|
||||
'strong' => true,
|
||||
)
|
||||
);
|
||||
?>
|
||||
</p>
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Notice: edd-requirement
|
||||
*
|
||||
* @package EDD_Stripe\Admin\Notices
|
||||
* @copyright Copyright (c) 2021, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.1
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'Credit card payments with Stripe are currently disabled.', 'easy-digital-downloads' ); ?></strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening strong tag, do not translate. %2$s Closing strong tag, do not translate. %3$s Opening code tag, do not translate. %4$s Closing code tag, do not translate. */
|
||||
__( 'To continue accepting credit card payments with Stripe please update %1$sEasy Digital Downloads%2$s to version %3$s2.11%4$s or higher.', 'easy-digital-downloads' ),
|
||||
'<strong>',
|
||||
'</strong>',
|
||||
'<code>',
|
||||
'</code>'
|
||||
),
|
||||
array(
|
||||
'code' => true,
|
||||
'strong' => true,
|
||||
)
|
||||
);
|
||||
?>
|
||||
</p>
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Notice: edd-stripe-core
|
||||
*
|
||||
* @package EDD_Stripe\Admin\Notices
|
||||
* @copyright Copyright (c) 2021, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.1
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$gateways_url = add_query_arg(
|
||||
array(
|
||||
'post_type' => 'download',
|
||||
'page' => 'edd-settings',
|
||||
'tab' => 'gateways',
|
||||
),
|
||||
admin_url( 'edit.php' )
|
||||
);
|
||||
?>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php esc_html_e( 'Accept credit card payments with Stripe', 'easy-digital-downloads' ); ?>
|
||||
</strong> <br />
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. %3$s Opening anchor tag, do not translate. %4$s Closing anchor tag, do not translate. */
|
||||
__( 'Easy Digital Downloads now lets you accept credit card payments using Stripe, including Apple Pay and Google Pay support. %1$sEnable Stripe%2$s now or %3$slearn more%4$s about the benefits of using Stripe.', 'easy-digital-downloads' ),
|
||||
'<a href="' . esc_url( $gateways_url ) . '">',
|
||||
'</a>',
|
||||
'<a href="https://easydigitaldownloads.com/edd-stripe-integration" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
'target' => true,
|
||||
),
|
||||
)
|
||||
);
|
||||
?>
|
||||
</p>
|
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* Notice: php-requirement
|
||||
*
|
||||
* @package EDD_Stripe\Admin\Notices
|
||||
* @copyright Copyright (c) 2021, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.1
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$required_version = 5.6;
|
||||
$current_version = phpversion();
|
||||
?>
|
||||
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'Credit card payments with Stripe are currently disabled.', 'easy-digital-downloads' ); ?></strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Future PHP version requirement. %2$s Current PHP version. %3$s Opening strong tag, do not translate. %4$s Closing strong tag, do not translate. %5$s Opening anchor tag, do not translate. %6$s Closing anchor tag, do not translate. */
|
||||
__( 'Easy Digital Downloads Stripe Payment Gateway requires PHP version %1$s or higher. It looks like you\'re using version %2$s, which means you will need to %3$supgrade your version of PHP to allow the plugin to continue to function%4$s. Newer versions of PHP are both faster and more secure. The version you\'re using %5$sno longer receives security updates%6$s, which is another great reason to update.', 'easy-digital-downloads' ),
|
||||
'<code>' . $required_version . '</code>',
|
||||
'<code>' . $current_version . '</code>',
|
||||
'<strong>',
|
||||
'</strong>',
|
||||
'<a href="http://php.net/eol.php" rel="noopener noreferrer" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'code' => true,
|
||||
'strong' => true,
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
'target' => true,
|
||||
)
|
||||
)
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button id="edds-php-read-more" class="button button-secondary button-small"><?php esc_html_e( 'Read More', 'easy-digital-downloads' ); ?></button>
|
||||
|
||||
<script>
|
||||
document.getElementById( 'edds-php-read-more' ).addEventListener( 'click', function( e ) {
|
||||
e.preventDefault();
|
||||
var wrapperEl = e.target.parentNode.nextElementSibling;
|
||||
wrapperEl.style.display = 'block' === wrapperEl.style.display ? 'none' : 'block';
|
||||
} );
|
||||
</script>
|
||||
</p>
|
||||
|
||||
<div style="display: none;">
|
||||
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'Which version should I upgrade to?', 'easy-digital-downloads' ); ?></strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Future PHP version requirement. */
|
||||
__( 'In order to be compatible with future versions of the Stripe payment gateway, you should update your PHP version to at least %1$s; however we recommend using version <code>7.4</code> if possible to receive the full speed and security benefits provided to more modern and fully supported versions of PHP. However, some plugins may not be fully compatible with PHP <code>7.4</code>, so more testing may be required.', 'easy-digital-downloads' ),
|
||||
'<code>' . $required_version . '</code>'
|
||||
),
|
||||
array(
|
||||
'code' => true,
|
||||
)
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'Need help upgrading? Ask your web host!', 'easy-digital-downloads' ); ?></strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__( 'Many web hosts can give you instructions on how/where to upgrade your version of PHP through their control panel, or may even be able to do it for you. %1$sRead more about updating PHP%2$s.', 'easy-digital-downloads' ),
|
||||
'<a href="https://wordpress.org/support/update-php/" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
'target' => true,
|
||||
)
|
||||
)
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
</div>
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* Reporting: Stripe
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class EDD_Stripe_Reports
|
||||
*
|
||||
* Do nothing in 2.8.0
|
||||
* The reports have not collected data since 2.7.0 and provide no tangible value.
|
||||
*
|
||||
* @since 2.6
|
||||
* @deprecated 2.8.0
|
||||
*/
|
||||
class EDD_Stripe_Reports {
|
||||
public function __construct() {
|
||||
_doing_it_wrong(
|
||||
__CLASS__,
|
||||
__( 'Stripe-specific reports have been removed.', 'easy-digital-downloads' ),
|
||||
'2.8.0'
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,401 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Register our settings section
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function edds_settings_section( $sections ) {
|
||||
$sections['edd-stripe'] = __( 'Stripe', 'easy-digital-downloads' );
|
||||
|
||||
return $sections;
|
||||
}
|
||||
add_filter( 'edd_settings_sections_gateways', 'edds_settings_section' );
|
||||
|
||||
/**
|
||||
* Register the gateway settings
|
||||
*
|
||||
* @access public
|
||||
* @since 1.0
|
||||
* @return array
|
||||
*/
|
||||
function edds_add_settings( $settings ) {
|
||||
// Output a placeholder setting to help promote Stripe
|
||||
// for non-Pro installs that do not meet PHP requirements.
|
||||
if (
|
||||
false === edds_has_met_requirements( 'php' ) &&
|
||||
false === edds_is_pro()
|
||||
) {
|
||||
return array_merge(
|
||||
$settings,
|
||||
array(
|
||||
'edd-stripe' => array(
|
||||
'edds-requirements-not-met' => array(
|
||||
'id' => 'edds-requirements-not-met',
|
||||
'name' => __( 'Unmet Requirements', 'easy-digital-downloads' ),
|
||||
'type' => 'stripe_requirements_not_met',
|
||||
'class' => 'edds-requirements-not-met',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$stripe_settings = array(
|
||||
'stripe_connect_button' => array(
|
||||
'id' => 'stripe_connect_button',
|
||||
'name' => __( 'Connection Status', 'easy-digital-downloads' ),
|
||||
'desc' => edds_stripe_connect_setting_field(),
|
||||
'type' => 'descriptive_text',
|
||||
'class' => 'edd-stripe-connect-row',
|
||||
),
|
||||
'test_publishable_key' => array(
|
||||
'id' => 'test_publishable_key',
|
||||
'name' => __( 'Test Publishable Key', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Enter your test publishable key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
|
||||
'type' => 'text',
|
||||
'size' => 'regular',
|
||||
'class' => 'edd-hidden edds-api-key-row',
|
||||
),
|
||||
'test_secret_key' => array(
|
||||
'id' => 'test_secret_key',
|
||||
'name' => __( 'Test Secret Key', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Enter your test secret key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
|
||||
'type' => 'text',
|
||||
'size' => 'regular',
|
||||
'class' => 'edd-hidden edds-api-key-row',
|
||||
),
|
||||
'live_publishable_key' => array(
|
||||
'id' => 'live_publishable_key',
|
||||
'name' => __( 'Live Publishable Key', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Enter your live publishable key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
|
||||
'type' => 'text',
|
||||
'size' => 'regular',
|
||||
'class' => 'edd-hidden edds-api-key-row',
|
||||
),
|
||||
'live_secret_key' => array(
|
||||
'id' => 'live_secret_key',
|
||||
'name' => __( 'Live Secret Key', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Enter your live secret key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
|
||||
'type' => 'text',
|
||||
'size' => 'regular',
|
||||
'class' => 'edd-hidden edds-api-key-row',
|
||||
),
|
||||
'stripe_webhook_description' => array(
|
||||
'id' => 'stripe_webhook_description',
|
||||
'type' => 'descriptive_text',
|
||||
'name' => __( 'Webhooks', 'easy-digital-downloads' ),
|
||||
'desc' =>
|
||||
'<p>' . sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__( 'In order for Stripe to function completely, you must configure your Stripe webhooks. Visit your %1$saccount dashboard%2$s to configure them. Please add a webhook endpoint for the URL below.', 'easy-digital-downloads' ),
|
||||
'<a href="https://dashboard.stripe.com/account/webhooks" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
) . '</p>' .
|
||||
'<p><strong>' . sprintf(
|
||||
/* translators: %s Webhook URL. Do not translate. */
|
||||
__( 'Webhook URL: %s', 'easy-digital-downloads' ),
|
||||
home_url( 'index.php?edd-listener=stripe' )
|
||||
) . '</strong></p>' .
|
||||
'<p>' . sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__( 'See our %1$sdocumentation%2$s for more information.', 'easy-digital-downloads' ),
|
||||
'<a href="' . esc_url( edds_documentation_route( 'stripe-webhooks' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
) . '</p>'
|
||||
),
|
||||
'stripe_billing_fields' => array(
|
||||
'id' => 'stripe_billing_fields',
|
||||
'name' => __( 'Billing Address Display', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Select how you would like to display the billing address fields on the checkout form. <p><strong>Notes</strong>:</p><p>If taxes are enabled, this option cannot be changed from "Full address".</p><p>If set to "No address fields", you <strong>must</strong> disable "zip code verification" in your Stripe account.</p>', 'easy-digital-downloads' ),
|
||||
'type' => 'select',
|
||||
'options' => array(
|
||||
'full' => __( 'Full address', 'easy-digital-downloads' ),
|
||||
'zip_country' => __( 'Zip / Postal Code and Country only', 'easy-digital-downloads' ),
|
||||
'none' => __( 'No address fields', 'easy-digital-downloads' )
|
||||
),
|
||||
'std' => 'full'
|
||||
),
|
||||
'stripe_statement_descriptor' => array(
|
||||
'id' => 'stripe_statement_descriptor',
|
||||
'name' => __( 'Statement Descriptor', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Choose how charges will appear on customer\'s credit card statements. <em>Max 22 characters</em>', 'easy-digital-downloads' ),
|
||||
'type' => 'text',
|
||||
),
|
||||
'stripe_use_existing_cards' => array(
|
||||
'id' => 'stripe_use_existing_cards',
|
||||
'name' => __( 'Show Previously Used Cards', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Provides logged in customers with a list of previous used payment methods for faster checkout.', 'easy-digital-downloads' ),
|
||||
'type' => 'checkbox'
|
||||
),
|
||||
'stripe_allow_prepaid' => array(
|
||||
'id' => 'stripe_allow_prepaid',
|
||||
'name' => __( 'Prepaid Cards', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Allow prepaid cards as valid payment method.', 'easy-digital-downloads' ),
|
||||
'type' => 'checkbox',
|
||||
),
|
||||
'stripe_split_payment_fields' => array(
|
||||
'id' => 'stripe_split_payment_fields',
|
||||
'name' => __( 'Split Credit Card Form', 'easy-digital-downloads' ),
|
||||
'desc' => __( 'Use separate card number, expiration, and CVC fields in payment forms.', 'easy-digital-downloads' ),
|
||||
'type' => 'checkbox',
|
||||
),
|
||||
'stripe_restrict_assets' => array(
|
||||
'id' => 'stripe_restrict_assets',
|
||||
'name' => ( __( 'Restrict Stripe Assets', 'easy-digital-downloads' ) ),
|
||||
'desc' => ( __( 'Only load Stripe.com hosted assets on pages that specifically utilize Stripe functionality.', 'easy-digital-downloads' ) ),
|
||||
'type' => 'checkbox',
|
||||
'tooltip_title' => __( 'Loading Javascript from Stripe', 'easy-digital-downloads' ),
|
||||
'tooltip_desc' => __( 'Stripe advises that their Javascript library be loaded on every page to take advantage of their advanced fraud detection rules. If you are not concerned with this, enable this setting to only load the Javascript when necessary. Read more about Stripe\'s recommended setup here: https://stripe.com/docs/web/setup.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( edd_get_option( 'stripe_checkout' ) ) {
|
||||
$stripe_settings['stripe_checkout'] = array(
|
||||
'id' => 'stripe_checkout',
|
||||
'name' => '<strong>' . __( 'Stripe Checkout', 'easy-digital-downloads' ) . '</strong>',
|
||||
'type' => 'stripe_checkout_notice',
|
||||
'desc' => wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
esc_html__( 'To ensure your website is compliant with the new %1$sStrong Customer Authentication%2$s (SCA) regulations, the legacy Stripe Checkout modal is no longer supported. Payments are still securely accepted through through Stripe on the standard Easy Digital Downloads checkout page. "Buy Now" buttons will also automatically redirect to the standard checkout page.', 'easy-digital-downloads' ),
|
||||
'<a href="https://stripe.com/en-ca/guides/strong-customer-authentication" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
'target' => true,
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ( version_compare( EDD_VERSION, 2.5, '>=' ) ) {
|
||||
$stripe_settings = array( 'edd-stripe' => $stripe_settings );
|
||||
|
||||
// Set up the new setting field for the Test Mode toggle notice
|
||||
$notice = array(
|
||||
'stripe_connect_test_mode_toggle_notice' => array(
|
||||
'id' => 'stripe_connect_test_mode_toggle_notice',
|
||||
'desc' => '<p>' . __( 'You have disabled the "Test Mode" option. Once you have saved your changes, please verify your Stripe connection, especially if you have not previously connected in with "Test Mode" disabled.', 'easy-digital-downloads' ) . '</p>',
|
||||
'type' => 'stripe_connect_notice',
|
||||
'field_class' => 'edd-hidden',
|
||||
)
|
||||
);
|
||||
|
||||
// Insert the new setting after the Test Mode checkbox
|
||||
$position = array_search( 'test_mode', array_keys( $settings['main'] ), true );
|
||||
$settings = array_merge(
|
||||
array_slice( $settings['main'], $position, 1, true ),
|
||||
$notice,
|
||||
$settings
|
||||
);
|
||||
}
|
||||
|
||||
return array_merge( $settings, $stripe_settings );
|
||||
}
|
||||
add_filter( 'edd_settings_gateways', 'edds_add_settings' );
|
||||
|
||||
/**
|
||||
* Force full billing address display when taxes are enabled
|
||||
*
|
||||
* @access public
|
||||
* @since 2.5
|
||||
* @return string
|
||||
*/
|
||||
function edd_stripe_sanitize_stripe_billing_fields_save( $value, $key ) {
|
||||
|
||||
if( 'stripe_billing_fields' == $key && edd_use_taxes() ) {
|
||||
|
||||
$value = 'full';
|
||||
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
}
|
||||
add_filter( 'edd_settings_sanitize_select', 'edd_stripe_sanitize_stripe_billing_fields_save', 10, 2 );
|
||||
|
||||
/**
|
||||
* Filter the output of the statement descriptor option to add a max length to the text string
|
||||
*
|
||||
* @since 2.6
|
||||
* @param $html string The full html for the setting output
|
||||
* @param $args array The original arguments passed in to output the html
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function edd_stripe_max_length_statement_descriptor( $html, $args ) {
|
||||
if ( 'stripe_statement_descriptor' !== $args['id'] ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
$html = str_replace( '<input type="text"', '<input type="text" maxlength="22"', $html );
|
||||
|
||||
return $html;
|
||||
}
|
||||
add_filter( 'edd_after_setting_output', 'edd_stripe_max_length_statement_descriptor', 10, 2 );
|
||||
|
||||
/**
|
||||
* Callback for the stripe_connect_notice field type.
|
||||
*
|
||||
* @since 2.6.14
|
||||
*
|
||||
* @param array $args The setting field arguments.
|
||||
*/
|
||||
function edd_stripe_connect_notice_callback( $args ) {
|
||||
|
||||
$value = isset( $args['desc'] ) ? $args['desc'] : '';
|
||||
|
||||
$class = edd_sanitize_html_class( $args['field_class'] );
|
||||
|
||||
?>
|
||||
<div class="<?php echo esc_attr( $class ); ?>" id="edd_settings[<?php echo edd_sanitize_key( $args['id'] ); ?>]">
|
||||
<?php echo wp_kses_post( $value ); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the stripe_checkout_notice field type.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array $args The setting field arguments
|
||||
*/
|
||||
function edd_stripe_checkout_notice_callback( $args ) {
|
||||
$value = isset( $args['desc'] ) ? $args['desc'] : '';
|
||||
|
||||
$html = '<div class="notice notice-warning inline' . edd_sanitize_html_class( $args['field_class'] ) . '" id="edd_settings[' . edd_sanitize_key( $args['id'] ) . ']">' . wpautop( $value ) . '</div>';
|
||||
|
||||
echo $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs information when Stripe has been activated but application requirements are not met.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*/
|
||||
function edd_stripe_requirements_not_met_callback() {
|
||||
$required_version = 5.6;
|
||||
$current_version = phpversion();
|
||||
|
||||
echo '<div class="notice inline notice-warning">';
|
||||
echo '<p>';
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Future PHP version requirement. %2$s Current PHP version. %3$s Opening strong tag, do not translate. %4$s Closing strong tag, do not translate. */
|
||||
__(
|
||||
'Processing credit cards with Stripe requires PHP version %1$s or higher. It looks like you\'re using version %2$s, which means you will need to %3$supgrade your version of PHP before acceping credit card payments%4$s.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
'<code>' . $required_version . '</code>',
|
||||
'<code>' . $current_version . '</code>',
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
array(
|
||||
'code' => true,
|
||||
'strong' => true
|
||||
)
|
||||
);
|
||||
echo '</p>';
|
||||
echo '<p>';
|
||||
|
||||
echo '<strong>';
|
||||
esc_html_e( 'Need help upgrading? Ask your web host!', 'easy-digital-downloads' );
|
||||
echo '</strong><br />';
|
||||
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__(
|
||||
'Many web hosts can give you instructions on how/where to upgrade your version of PHP through their control panel, or may even be able to do it for you. If you need to change hosts, please see %1$sour hosting recommendations%2$s.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
'<a href="https://easydigitaldownloads.com/recommended-wordpress-hosting/" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'target' => true,
|
||||
'rel' => true,
|
||||
),
|
||||
)
|
||||
);
|
||||
echo '</p>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a notice to the "Payment Gateways" selector if Stripe has been activated but does
|
||||
* not meet application requirements.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @param string $html Setting HTML.
|
||||
* @param array $args Setting arguments.
|
||||
* @return string
|
||||
*/
|
||||
function edds_payment_gateways_notice( $html, $args ) {
|
||||
if ( 'gateways' !== $args['id'] ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
if (
|
||||
true === edds_is_pro() ||
|
||||
true === edds_has_met_requirements( 'php' )
|
||||
) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
$required_version = 5.6;
|
||||
$current_version = phpversion();
|
||||
|
||||
$html .= '<div id="edds-payment-gateways-stripe-unmet-requirements" class="notice inline notice-info"><p>' .
|
||||
wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s PHP version requirement. %2$s Current PHP version. %3$s Opening strong tag, do not translate. %4$s Closing strong tag, do not translate. */
|
||||
__(
|
||||
'Processing credit cards with Stripe requires PHP version %1$s or higher. It looks like you\'re using version %2$s, which means you will need to %3$supgrade your version of PHP before acceping credit card payments%4$s.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
'<code>' . $required_version . '</code>',
|
||||
'<code>' . $current_version . '</code>',
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
array(
|
||||
'code' => true,
|
||||
'strong' => true
|
||||
)
|
||||
) .
|
||||
'</p><p><strong>' .
|
||||
esc_html__( 'Need help upgrading? Ask your web host!', 'easy-digital-downloads' ) .
|
||||
'</strong><br />' .
|
||||
wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__(
|
||||
'Many web hosts can give you instructions on how/where to upgrade your version of PHP through their control panel, or may even be able to do it for you. If you need to change hosts, please see %1$sour hosting recommendations%2$s.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
'<a href="https://easydigitaldownloads.com/recommended-wordpress-hosting/" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'target' => true,
|
||||
'rel' => true,
|
||||
),
|
||||
)
|
||||
) . '</p></div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
add_filter( 'edd_after_setting_output', 'edds_payment_gateways_notice', 10, 2 );
|
@ -0,0 +1,734 @@
|
||||
<?php
|
||||
/*
|
||||
* Admin Settings: Stripe Connect
|
||||
*
|
||||
* @package EDD_Stripe\Admin\Settings\Stripe_Connect
|
||||
* @copyright Copyright (c) 2019, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the Stripe API keys can be managed manually.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function edds_stripe_connect_can_manage_keys() {
|
||||
|
||||
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id', false );
|
||||
$secret = edd_is_test_mode() ? edd_get_option( 'test_secret_key' ) : edd_get_option( 'live_secret_key' );
|
||||
|
||||
return empty( $stripe_connect_account_id ) && $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a URL to allow Stripe Connect via oAuth.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function edds_stripe_connect_url() {
|
||||
$return_url = add_query_arg(
|
||||
array(
|
||||
'post_type' => 'download',
|
||||
'page' => 'edd-settings',
|
||||
'tab' => 'gateways',
|
||||
'section' => 'edd-stripe',
|
||||
),
|
||||
admin_url( 'edit.php' )
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the URL users are returned to after using Stripe Connect oAuth.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param $return_url URL to return to.
|
||||
*/
|
||||
$return_url = apply_filters( 'edds_stripe_connect_return_url', $return_url );
|
||||
|
||||
$stripe_connect_url = add_query_arg(
|
||||
array(
|
||||
'live_mode' => (int) ! edd_is_test_mode(),
|
||||
'state' => str_pad( wp_rand( wp_rand(), PHP_INT_MAX ), 100, wp_rand(), STR_PAD_BOTH ),
|
||||
'customer_site_url' => esc_url_raw( $return_url ),
|
||||
),
|
||||
'https://easydigitaldownloads.com/?edd_gateway_connect_init=stripe_connect'
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the URL to start the Stripe Connect oAuth flow.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param $stripe_connect_url URL to oAuth proxy.
|
||||
*/
|
||||
$stripe_connect_url = apply_filters( 'edds_stripe_connect_url', $stripe_connect_url );
|
||||
|
||||
return $stripe_connect_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for Stripe Connect completion requests and saves the Stripe API keys.
|
||||
*
|
||||
* @since 2.6.14
|
||||
*/
|
||||
function edds_process_gateway_connect_completion() {
|
||||
|
||||
if( ! isset( $_GET['edd_gateway_connect_completion'] ) || 'stripe_connect' !== $_GET['edd_gateway_connect_completion'] || ! isset( $_GET['state'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( ! current_user_can( 'manage_shop_settings' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( headers_sent() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$edd_credentials_url = add_query_arg( array(
|
||||
'live_mode' => (int) ! edd_is_test_mode(),
|
||||
'state' => sanitize_text_field( $_GET['state'] ),
|
||||
'customer_site_url' => admin_url( 'edit.php?post_type=download' ),
|
||||
), 'https://easydigitaldownloads.com/?edd_gateway_connect_credentials=stripe_connect' );
|
||||
|
||||
$response = wp_remote_get( esc_url_raw( $edd_credentials_url ) );
|
||||
|
||||
if( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
$message = '<p>' . sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__( 'There was an error getting your Stripe credentials. Please %1$stry again%2$s. If you continue to have this problem, please contact support.', 'easy-digital-downloads' ),
|
||||
'<a href="' . esc_url( admin_url( 'edit.php?post_type=download&page=edd-settings&tab=gateways§ion=edd-stripe' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
) . '</p>';
|
||||
wp_die( $message );
|
||||
}
|
||||
|
||||
$data = json_decode( $response['body'], true );
|
||||
$data = $data['data'];
|
||||
|
||||
if( edd_is_test_mode() ) {
|
||||
edd_update_option( 'test_publishable_key', sanitize_text_field( $data['publishable_key'] ) );
|
||||
edd_update_option( 'test_secret_key', sanitize_text_field( $data['secret_key'] ) );
|
||||
} else {
|
||||
edd_update_option( 'live_publishable_key', sanitize_text_field( $data['publishable_key'] ) );
|
||||
edd_update_option( 'live_secret_key', sanitize_text_field( $data['secret_key'] ) );
|
||||
}
|
||||
|
||||
edd_update_option( 'stripe_connect_account_id', sanitize_text_field( $data['stripe_user_id'] ) );
|
||||
wp_redirect( esc_url_raw( admin_url( 'edit.php?post_type=download&page=edd-settings&tab=gateways§ion=edd-stripe' ) ) );
|
||||
exit;
|
||||
|
||||
}
|
||||
add_action( 'admin_init', 'edds_process_gateway_connect_completion' );
|
||||
|
||||
/**
|
||||
* Returns a URL to disconnect the current Stripe Connect account ID and keys.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return string $stripe_connect_disconnect_url URL to disconnect an account ID and keys.
|
||||
*/
|
||||
function edds_stripe_connect_disconnect_url() {
|
||||
$stripe_connect_disconnect_url = add_query_arg(
|
||||
array(
|
||||
'post_type' => 'download',
|
||||
'page' => 'edd-settings',
|
||||
'tab' => 'gateways',
|
||||
'section' => 'edd-stripe',
|
||||
'edds-stripe-disconnect' => true,
|
||||
),
|
||||
admin_url( 'edit.php' )
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the URL to "disconnect" the Stripe Account.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param $stripe_connect_disconnect_url URL to remove the associated Account ID.
|
||||
*/
|
||||
$stripe_connect_disconnect_url = apply_filters(
|
||||
'edds_stripe_connect_disconnect_url',
|
||||
$stripe_connect_disconnect_url
|
||||
);
|
||||
|
||||
$stripe_connect_disconnect_url = wp_nonce_url( $stripe_connect_disconnect_url, 'edds-stripe-connect-disconnect' );
|
||||
|
||||
return $stripe_connect_disconnect_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the associated Stripe Connect Account ID and keys.
|
||||
*
|
||||
* This does not revoke application permissions from the Stripe Dashboard,
|
||||
* it simply allows the "Connect with Stripe" flow to run again for a different account.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_stripe_connect_process_disconnect() {
|
||||
// Do not need to handle this request, bail.
|
||||
if (
|
||||
! ( isset( $_GET['page'] ) && 'edd-settings' === $_GET['page'] ) ||
|
||||
! isset( $_GET['edds-stripe-disconnect'] )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Current user cannot handle this request, bail.
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No nonce, bail.
|
||||
if ( ! isset( $_GET['_wpnonce'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Invalid nonce, bail.
|
||||
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'edds-stripe-connect-disconnect' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options = array(
|
||||
'stripe_connect_account_id',
|
||||
'stripe_connect_account_country',
|
||||
'test_publishable_key',
|
||||
'test_secret_key',
|
||||
'live_publishable_key',
|
||||
'live_secret_key',
|
||||
);
|
||||
|
||||
foreach ( $options as $option ) {
|
||||
edd_delete_option( $option );
|
||||
}
|
||||
|
||||
$redirect = remove_query_arg(
|
||||
array(
|
||||
'_wpnonce',
|
||||
'edds-stripe-disconnect',
|
||||
)
|
||||
);
|
||||
|
||||
return wp_redirect( esc_url_raw( $redirect ) );
|
||||
}
|
||||
add_action( 'admin_init', 'edds_stripe_connect_process_disconnect' );
|
||||
|
||||
/**
|
||||
* Updates the `stripe_connect_account_country` setting if using Stripe Connect
|
||||
* and no country information is available.
|
||||
*
|
||||
* @since 2.8.7
|
||||
*/
|
||||
function edds_stripe_connect_maybe_refresh_account_country() {
|
||||
// Current user cannot modify options, bail.
|
||||
if ( false === current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stripe Connect has not been used, bail.
|
||||
$account_id = edd_get_option( 'stripe_connect_account_id', '' );
|
||||
|
||||
if ( empty( $account_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Account country is already set, bail.
|
||||
$account_country = edd_get_option( 'stripe_connect_account_country', '' );
|
||||
|
||||
if ( ! empty( $account_country ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$account = edds_api_request( 'Account', 'retrieve', $account_id );
|
||||
|
||||
if ( isset( $account->country ) ) {
|
||||
$account_country = sanitize_text_field(
|
||||
strtolower( $account->country )
|
||||
);
|
||||
|
||||
edd_update_option(
|
||||
'stripe_connect_account_country',
|
||||
$account_country
|
||||
);
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
add_action( 'admin_init', 'edds_stripe_connect_maybe_refresh_account_country' );
|
||||
|
||||
/**
|
||||
* Renders custom HTML for the "Stripe Connect" setting field in the Stripe Payment Gateway
|
||||
* settings subtab.
|
||||
*
|
||||
* Provides a way to use Stripe Connect and manually manage API keys.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_stripe_connect_setting_field() {
|
||||
$stripe_connect_url = edds_stripe_connect_url();
|
||||
$stripe_disconnect_url = edds_stripe_connect_disconnect_url();
|
||||
|
||||
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id' );
|
||||
|
||||
$api_key = edd_is_test_mode()
|
||||
? edd_get_option( 'test_publishable_key' )
|
||||
: edd_get_option( 'live_publishable_key' );
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<?php if ( empty( $api_key ) ) : ?>
|
||||
|
||||
<a href="<?php echo esc_url( $stripe_connect_url ); ?>" class="edd-stripe-connect">
|
||||
<span><?php esc_html_e( 'Connect with Stripe', 'easy-digital-downloads' ); ?></span>
|
||||
</a>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
/** This filter is documented in includes/admin/settings/stripe-connect.php */
|
||||
$show_fee_message = apply_filters( 'edds_show_stripe_connect_fee_message', true );
|
||||
|
||||
$fee_message = true === $show_fee_message
|
||||
? (
|
||||
__(
|
||||
'Connect with Stripe for pay as you go pricing: 2% per-transaction fee + Stripe fees.',
|
||||
'easy-digital-downloads'
|
||||
) . ' '
|
||||
)
|
||||
: '';
|
||||
|
||||
echo esc_html( $fee_message );
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__( 'Have questions about connecting with Stripe? See the %1$sdocumentation%2$s.', 'easy-digital-downloads' ),
|
||||
'<a href="' . esc_url( edds_documentation_route( 'stripe-connect' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'target' => true,
|
||||
'rel' => true,
|
||||
)
|
||||
)
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( ! empty( $api_key ) ) : ?>
|
||||
|
||||
<div
|
||||
id="edds-stripe-connect-account"
|
||||
class="edds-stripe-connect-acount-info notice inline"
|
||||
data-account-id="<?php echo esc_attr( $stripe_connect_account_id ); ?>"
|
||||
data-nonce="<?php echo wp_create_nonce( 'edds-stripe-connect-account-information' ); ?>"
|
||||
>
|
||||
<p><span class="spinner is-active"></span>
|
||||
<em><?php esc_html_e( 'Retrieving account information...', 'easy-digital-downloads' ); ?></em>
|
||||
</div>
|
||||
<div id="edds-stripe-disconnect-reconnect">
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( true === edds_stripe_connect_can_manage_keys() ) : ?>
|
||||
|
||||
<div class="edds-api-key-toggle">
|
||||
<p>
|
||||
<button type="button" class="button-link">
|
||||
<small>
|
||||
<?php esc_html_e( 'Manage API keys manually', 'easy-digital-downloads' ); ?>
|
||||
</small>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="edds-api-key-toggle edd-hidden">
|
||||
<p>
|
||||
<button type="button" class="button-link">
|
||||
<small>
|
||||
<?php esc_html_e( 'Hide API keys', 'easy-digital-downloads' ); ?>
|
||||
</small>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
<div class="notice inline notice-warning" style="margin: 15px 0 -10px;">
|
||||
<?php echo wpautop( esc_html__( 'Although you can add your API keys manually, we recommend using Stripe Connect: an easier and more secure way of connecting your Stripe account to your website. Stripe Connect prevents issues that can arise when copying and pasting account details from Stripe into your Easy Digital Downloads payment gateway settings. With Stripe Connect you\'ll be ready to go with just a few clicks.', 'easy-digital-downloads' ) ); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to an AJAX request about the current Stripe connection status.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_stripe_connect_account_info_ajax_response() {
|
||||
// Generic error.
|
||||
$unknown_error = array(
|
||||
'message' => wpautop( esc_html__( 'Unable to retrieve account information.', 'easy-digital-downloads' ) ),
|
||||
);
|
||||
|
||||
// Current user can't manage settings.
|
||||
if ( ! current_user_can( 'manage_shop_settings' ) ) {
|
||||
return wp_send_json_error( $unknown_error );
|
||||
}
|
||||
|
||||
// Nonce validation, show error on fail.
|
||||
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'edds-stripe-connect-account-information' ) ) {
|
||||
return wp_send_json_error( $unknown_error );
|
||||
}
|
||||
|
||||
$account_id = isset( $_POST['accountId'] )
|
||||
? sanitize_text_field( $_POST['accountId'] )
|
||||
: '';
|
||||
|
||||
$mode = edd_is_test_mode()
|
||||
? _x( 'test', 'Stripe Connect mode', 'easy-digital-downloads' )
|
||||
: _x( 'live', 'Stripe Connect mode', 'easy-digital-downloads' );
|
||||
|
||||
// Provides general reconnect and disconnect action URLs.
|
||||
$reconnect_disconnect_actions = wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Stripe payment mode. %2$s Opening anchor tag for reconnecting to Stripe, do not translate. %3$s Opening anchor tag for disconnecting Stripe, do not translate. %4$s Closing anchor tag, do not translate. */
|
||||
__( 'Your Stripe account is connected in %1$s mode. %2$sReconnect in %1$s mode%4$s, or %3$sdisconnect this account%4$s.', 'easy-digital-downloads' ),
|
||||
'<strong>' . $mode . '</strong>',
|
||||
'<a href="' . esc_url( edds_stripe_connect_url() ) . '" rel="noopener noreferrer">',
|
||||
'<a href="' . esc_url( edds_stripe_connect_disconnect_url() ) . '">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'strong' => true,
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// If connecting in Test Mode Stripe gives you the opportunity to create a
|
||||
// temporary account. Alert the user of the limitations associated with
|
||||
// this type of account.
|
||||
$dev_account_error = array(
|
||||
'message' => wp_kses(
|
||||
wpautop(
|
||||
sprintf(
|
||||
__(
|
||||
/* translators: %1$s Opening bold tag, do not translate. %2$s Closing bold tag, do not translate. */
|
||||
'You are currently connected to a %1$stemporary%2$s Stripe test account, which can only be used for testing purposes. You cannot manage this account in Stripe.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
) . ' ' .
|
||||
(
|
||||
class_exists( 'EDD_Recurring' )
|
||||
? __(
|
||||
'Webhooks cannot be configured for recurring purchases with this account.',
|
||||
'easy-digital-downloads'
|
||||
)
|
||||
: ''
|
||||
) . ' ' .
|
||||
sprintf(
|
||||
__(
|
||||
/* translators: %1$s Opening link tag, do not translate. %2$s Closing link tag, do not translate. */
|
||||
'%1$sRegister a Stripe account%2$s for full access.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
'<a href="https://dashboard.stripe.com/register" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
) . ' ' .
|
||||
'<br /><br />' .
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag for disconnecting Stripe, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__( '%1$sDisconnect this account%2$s.', 'easy-digital-downloads' ),
|
||||
'<a href="' . esc_url( edds_stripe_connect_disconnect_url() ) . '">',
|
||||
'</a>'
|
||||
)
|
||||
),
|
||||
array(
|
||||
'p' => true,
|
||||
'strong' => true,
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
'target' => true,
|
||||
)
|
||||
)
|
||||
),
|
||||
'status' => 'warning',
|
||||
);
|
||||
|
||||
// Attempt to show account information from Stripe Connect account.
|
||||
if ( ! empty( $account_id ) ) {
|
||||
try {
|
||||
$account = edds_api_request( 'Account', 'retrieve', $account_id );
|
||||
|
||||
// Find the email.
|
||||
$email = isset( $account->email )
|
||||
? esc_html( $account->email )
|
||||
: '';
|
||||
|
||||
// Find a Display Name.
|
||||
$display_name = isset( $account->display_name )
|
||||
? esc_html( $account->display_name )
|
||||
: '';
|
||||
|
||||
if (
|
||||
empty( $display_name ) &&
|
||||
isset( $account->settings ) &&
|
||||
isset( $account->settings->dashboard ) &&
|
||||
isset( $account->settings->dashboard->display_name )
|
||||
) {
|
||||
$display_name = esc_html( $account->settings->dashboard->display_name );
|
||||
}
|
||||
|
||||
// Unsaved/unactivated accounts do not have an email or display name.
|
||||
if ( empty( $email ) && empty( $display_name ) ) {
|
||||
return wp_send_json_success( $dev_account_error );
|
||||
}
|
||||
|
||||
if ( ! empty( $display_name ) ) {
|
||||
$display_name = '<strong>' . $display_name . '</strong><br/ >';
|
||||
}
|
||||
|
||||
if ( ! empty( $email ) ) {
|
||||
$email = $email . ' — ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters if the Stripe Connect fee messaging should show.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @param bool $show_fee_message Show fee message, or not.
|
||||
*/
|
||||
$show_fee_message = apply_filters( 'edds_show_stripe_connect_fee_message', true );
|
||||
|
||||
$fee_message = true === $show_fee_message
|
||||
? wpautop(
|
||||
esc_html__(
|
||||
'Pay as you go pricing: 2% per-transaction fee + Stripe fees.',
|
||||
'easy-digital-downloads'
|
||||
)
|
||||
)
|
||||
: '';
|
||||
|
||||
// Return a message with name, email, and reconnect/disconnect actions.
|
||||
return wp_send_json_success(
|
||||
array(
|
||||
'message' => wpautop(
|
||||
// $display_name is already escaped
|
||||
$display_name . esc_html( $email ) . esc_html__( 'Administrator (Owner)', 'easy-digital-downloads' ) . $fee_message
|
||||
),
|
||||
'actions' => $reconnect_disconnect_actions,
|
||||
'status' => 'success',
|
||||
)
|
||||
);
|
||||
} catch ( \Stripe\Exception\AuthenticationException $e ) {
|
||||
// API keys were changed after using Stripe Connect.
|
||||
return wp_send_json_error(
|
||||
array(
|
||||
'message' => wpautop(
|
||||
esc_html__( 'The API keys provided do not match the Stripe Connect account associated with this installation. If you have manually modified these values after connecting your account, please reconnect below or update your API keys.', 'easy-digital-downloads' ) .
|
||||
'<br /><br />' .
|
||||
$reconnect_disconnect_actions
|
||||
),
|
||||
)
|
||||
);
|
||||
} catch ( \EDD_Stripe_Utils_Exceptions_Stripe_API_Unmet_Requirements $e ) {
|
||||
return wp_send_json_error(
|
||||
array(
|
||||
'message' => wpautop(
|
||||
$e->getMessage()
|
||||
),
|
||||
)
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
// General error.
|
||||
return wp_send_json_error( $unknown_error );
|
||||
}
|
||||
// Manual API key management.
|
||||
} else {
|
||||
$connect_button = sprintf(
|
||||
'<a href="%s" class="edd-stripe-connect"><span>%s</span></a>',
|
||||
esc_url( edds_stripe_connect_url() ),
|
||||
esc_html__( 'Connect with Stripe', 'easy-digital-downloads' )
|
||||
);
|
||||
|
||||
$connect = esc_html__( 'It is highly recommended to Connect with Stripe for easier setup and improved security.', 'easy-digital-downloads' );
|
||||
|
||||
// See if the keys are valid.
|
||||
try {
|
||||
// While we could show similar account information, leave it blank to help
|
||||
// push people towards Stripe Connect.
|
||||
$account = edds_api_request( 'Account', 'retrieve' );
|
||||
|
||||
return wp_send_json_success(
|
||||
array(
|
||||
'message' => wpautop(
|
||||
sprintf(
|
||||
/* translators: %1$s Stripe payment mode.*/
|
||||
__( 'Your manually managed %1$s mode API keys are valid.', 'easy-digital-downloads' ),
|
||||
'<strong>' . $mode . '</strong>'
|
||||
) .
|
||||
'<br /><br />' .
|
||||
$connect . '<br /><br />' . $connect_button
|
||||
),
|
||||
'status' => 'success',
|
||||
)
|
||||
);
|
||||
// Show invalid keys.
|
||||
} catch ( \Exception $e ) {
|
||||
return wp_send_json_error(
|
||||
array(
|
||||
'message' => wpautop(
|
||||
sprintf(
|
||||
/* translators: %1$s Stripe payment mode.*/
|
||||
__( 'Your manually managed %1$s mode API keys are invalid.', 'easy-digital-downloads' ),
|
||||
'<strong>' . $mode . '</strong>'
|
||||
) .
|
||||
'<br /><br />' .
|
||||
$connect . '<br /><br />' . $connect_button
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_stripe_connect_account_info', 'edds_stripe_connect_account_info_ajax_response' );
|
||||
|
||||
/**
|
||||
* Registers admin notices for Stripe Connect.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return true|WP_Error True if all notices are registered, otherwise WP_Error.
|
||||
*/
|
||||
function edds_stripe_connect_admin_notices_register() {
|
||||
$registry = edds_get_registry( 'admin-notices' );
|
||||
|
||||
if ( ! $registry ) {
|
||||
return new WP_Error( 'edds-invalid-registry', esc_html__( 'Unable to locate registry', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
$connect_button = sprintf(
|
||||
'<a href="%s" class="edd-stripe-connect"><span>%s</span></a>',
|
||||
esc_url( edds_stripe_connect_url() ),
|
||||
esc_html__( 'Connect with Stripe', 'easy-digital-downloads' )
|
||||
);
|
||||
|
||||
try {
|
||||
// Stripe Connect.
|
||||
$registry->add(
|
||||
'stripe-connect',
|
||||
array(
|
||||
'message' => sprintf(
|
||||
'<p>%s</p><p>%s</p>',
|
||||
esc_html__( 'Start accepting payments with Stripe by connecting your account. Stripe Connect helps ensure easier setup and improved security.', 'easy-digital-downloads' ),
|
||||
$connect_button
|
||||
),
|
||||
'type' => 'info',
|
||||
'dismissible' => true,
|
||||
)
|
||||
);
|
||||
|
||||
// Stripe Connect reconnect.
|
||||
/** translators: %s Test mode status. */
|
||||
$test_mode_status = edd_is_test_mode()
|
||||
? _x( 'enabled', 'gateway test mode status', 'easy-digital-downloads' )
|
||||
: _x( 'disabled', 'gateway test mode status', 'easy-digital-downloads' );
|
||||
|
||||
$registry->add(
|
||||
'stripe-connect-reconnect',
|
||||
array(
|
||||
'message' => sprintf(
|
||||
'<p>%s</p><p>%s</p>',
|
||||
sprintf(
|
||||
/* translators: %s Test mode status. Enabled or disabled. */
|
||||
__( '"Test Mode" has been %s. Please verify your Stripe connection status.', 'easy-digital-downloads' ),
|
||||
$test_mode_status
|
||||
),
|
||||
$connect_button
|
||||
),
|
||||
'type' => 'warning',
|
||||
'dismissible' => true,
|
||||
)
|
||||
);
|
||||
} catch( Exception $e ) {
|
||||
return new WP_Error( 'edds-invalid-notices-registration', esc_html__( $e->getMessage() ) );
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
add_action( 'admin_init', 'edds_stripe_connect_admin_notices_register' );
|
||||
|
||||
/**
|
||||
* Conditionally prints registered notices.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
function edds_stripe_connect_admin_notices_print() {
|
||||
// Current user needs capability to dismiss notices.
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$registry = edds_get_registry( 'admin-notices' );
|
||||
|
||||
if ( ! $registry ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = new EDD_Stripe_Admin_Notices( $registry );
|
||||
|
||||
wp_enqueue_script( 'edds-admin-notices' );
|
||||
|
||||
try {
|
||||
$enabled_gateways = edd_get_enabled_payment_gateways();
|
||||
|
||||
$api_key = true === edd_is_test_mode()
|
||||
? edd_get_option( 'test_secret_key' )
|
||||
: edd_get_option( 'live_secret_key' );
|
||||
|
||||
$mode_toggle = isset( $_GET['edd-message'] ) && 'connect-to-stripe' === $_GET['edd-message'];
|
||||
|
||||
if ( array_key_exists( 'stripe', $enabled_gateways ) && empty( $api_key ) ) {
|
||||
wp_enqueue_style(
|
||||
'edd-stripe-admin-styles',
|
||||
EDDSTRIPE_PLUGIN_URL . 'assets/css/build/admin.min.css',
|
||||
array(),
|
||||
EDD_STRIPE_VERSION
|
||||
);
|
||||
|
||||
// Stripe Connect.
|
||||
if ( false === $mode_toggle ) {
|
||||
$notices->output( 'stripe-connect' );
|
||||
// Stripe Connect reconnect.
|
||||
} else {
|
||||
$notices->output( 'stripe-connect-reconnect' );
|
||||
}
|
||||
}
|
||||
} catch( Exception $e ) {}
|
||||
}
|
||||
add_action( 'admin_notices', 'edds_stripe_connect_admin_notices_print' );
|
@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Handles automatic upgrades behind the scenes.
|
||||
*
|
||||
* @since 2.8.10
|
||||
*/
|
||||
add_action( 'admin_init', function() {
|
||||
/*
|
||||
* Move license data to new option, after product name change.
|
||||
* @link https://github.com/awesomemotive/edd-stripe/issues/715
|
||||
*/
|
||||
$license_key = edd_get_option( 'edd_stripe_payment_gateway_license_key' );
|
||||
if ( $license_key ) {
|
||||
edd_update_option( 'edd_stripe_pro_payment_gateway_license_key', sanitize_text_field( $license_key ) );
|
||||
edd_delete_option( 'edd_stripe_payment_gateway_license_key' );
|
||||
|
||||
$license_status = get_option( 'edd_stripe_payment_gateway_license_active' );
|
||||
if ( $license_status ) {
|
||||
update_option( 'edd_stripe_pro_payment_gateway_license_active', $license_status );
|
||||
delete_option( 'edd_stripe_payment_gateway_license_active' );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* Stripe Upgrade Notices
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
*/
|
||||
function edd_stripe_upgrade_notices() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
// Don't show notices on the upgrades page
|
||||
if ( isset( $_GET['page'] ) && $_GET['page'] == 'edd-upgrades' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! edd_has_upgrade_completed( 'stripe_customer_id_migration' ) ) {
|
||||
|
||||
$has_stripe_customers = $wpdb->get_var( "SELECT count(user_id) FROM $wpdb->usermeta WHERE meta_key IN ( '_edd_stripe_customer_id', '_edd_stripe_customer_id_test' ) LIMIT 1" );
|
||||
$needs_upgrade = ! empty( $has_stripe_customers );
|
||||
|
||||
if( ! $needs_upgrade ) {
|
||||
edd_set_upgrade_complete( 'stripe_customer_id_migration' );
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="updated">' .
|
||||
'<p>' .
|
||||
/* translators: %s Upgrade link. */
|
||||
__( 'Easy Digital Downloads - Stripe Gateway needs to upgrade the customers database; <a href="%s">click here to start the upgrade</a>. <a href="#" onClick="jQuery(this).parent().next(\'p\').slideToggle()">Learn more about this upgrade</a>', 'easy-digital-downloads' ) .
|
||||
'</p>' .
|
||||
'<p style="display: none;">' .
|
||||
__( '<strong>About this upgrade:</strong><br />This upgrade will improve the reliability of associating purchase records with your existing customer records in Stripe by changing their Stripe Customer IDs to be stored locally on their EDD customer record, instead of their user record.', 'easy-digital-downloads' ) .
|
||||
'<br /><br />' .
|
||||
__( '<strong>Advanced User?</strong><br />This upgrade can also be run via WPCLI with the following command:<br /><code>wp edd-stripe migrate_customer_ids</code>', 'easy-digital-downloads' ) .
|
||||
'</p>' .
|
||||
'</div>',
|
||||
esc_url( admin_url( 'index.php?page=edd-upgrades&edd-upgrade=stripe_customer_id_migration' ) )
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
add_action( 'admin_notices', 'edd_stripe_upgrade_notices' );
|
||||
|
||||
/**
|
||||
* Migrates Stripe Customer IDs from the usermeta table to the edd_customermeta table.
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_customer_id_migration() {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! current_user_can( 'manage_shop_settings' ) ) {
|
||||
wp_die( __( 'You do not have permission to do shop upgrades', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
|
||||
}
|
||||
|
||||
ignore_user_abort( true );
|
||||
|
||||
$step = isset( $_GET['step'] ) ? absint( $_GET['step'] ) : 1;
|
||||
$number = isset( $_GET['number'] ) ? absint( $_GET['number'] ) : 10;
|
||||
$offset = $step == 1 ? 0 : ( $step - 1 ) * $number;
|
||||
|
||||
$total = isset( $_GET['total'] ) ? absint( $_GET['total'] ) : false;
|
||||
if ( empty( $total ) || $total <= 1 ) {
|
||||
$total_sql = "SELECT COUNT(user_id) as total_users FROM $wpdb->usermeta WHERE meta_key IN ( '_edd_stripe_customer_id', '_edd_stripe_customer_id_test' )";
|
||||
$results = $wpdb->get_row( $total_sql );
|
||||
$total = $results->total_users;
|
||||
}
|
||||
|
||||
$stripe_user_meta = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM $wpdb->usermeta WHERE meta_key IN ( '_edd_stripe_customer_id', '_edd_stripe_customer_id_test' ) ORDER BY umeta_id ASC LIMIT %d,%d;",
|
||||
$offset,
|
||||
$number
|
||||
)
|
||||
);
|
||||
|
||||
if ( $stripe_user_meta ) {
|
||||
|
||||
foreach ( $stripe_user_meta as $stripe_user ) {
|
||||
|
||||
$user = get_userdata( $stripe_user->user_id );
|
||||
$email = $user->user_email;
|
||||
|
||||
$customer = new EDD_Customer( $email );
|
||||
|
||||
// If we don't have a customer on this site, just move along.
|
||||
if ( ! $customer->id > 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stripe_customer_id = $stripe_user->meta_value;
|
||||
|
||||
// We should try and use a recurring ID if one exists for this user
|
||||
if ( class_exists( 'EDD_Recurring_Subscriber' ) ) {
|
||||
$subscriber = new EDD_Recurring_Subscriber( $customer->id );
|
||||
$stripe_customer_id = $subscriber->get_recurring_customer_id( 'stripe' );
|
||||
}
|
||||
|
||||
$customer->update_meta( $stripe_user->meta_key, $stripe_customer_id );
|
||||
|
||||
}
|
||||
|
||||
$step ++;
|
||||
$redirect = add_query_arg( array(
|
||||
'page' => 'edd-upgrades',
|
||||
'edd-upgrade' => 'stripe_customer_id_migration',
|
||||
'step' => absint( $step ),
|
||||
'number' => absint( $number ),
|
||||
'total' => absint( $total ),
|
||||
), admin_url( 'index.php' ) );
|
||||
|
||||
wp_safe_redirect( $redirect );
|
||||
exit;
|
||||
|
||||
} else {
|
||||
|
||||
update_option( 'edds_stripe_version', preg_replace( '/[^0-9.].*/', '', EDD_STRIPE_VERSION ) );
|
||||
edd_set_upgrade_complete( 'stripe_customer_id_migration' );
|
||||
delete_option( 'edd_doing_upgrade' );
|
||||
|
||||
wp_redirect( admin_url() );
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
add_action( 'edd_stripe_customer_id_migration', 'edd_stripe_customer_id_migration' );
|
@ -0,0 +1,544 @@
|
||||
<?php
|
||||
/**
|
||||
* Card actions.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Process the card update actions from the manage card form.
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_process_card_update() {
|
||||
$enabled = edd_stripe_existing_cards_enabled();
|
||||
|
||||
// Feature not enabled.
|
||||
if ( ! $enabled ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'This feature is not available at this time.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Source can't be found.
|
||||
$payment_method = isset( $_POST['payment_method'] ) ? sanitize_text_field( $_POST['payment_method'] ) : '';
|
||||
|
||||
if ( empty( $payment_method ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Nonce failed.
|
||||
if ( ! edds_verify( 'nonce', $payment_method . '_update' ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
|
||||
if ( empty( $stripe_customer_id ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$card_args = array();
|
||||
$card_fields = array(
|
||||
'address_city',
|
||||
'address_country',
|
||||
'address_line1',
|
||||
'address_line2',
|
||||
'address_zip',
|
||||
'address_state',
|
||||
'exp_month',
|
||||
'exp_year',
|
||||
);
|
||||
|
||||
foreach ( $card_fields as $card_field ) {
|
||||
$card_args[ $card_field ] = ( isset( $_POST[ $card_field ] ) && '' !== $_POST[ $card_field ] )
|
||||
? sanitize_text_field( $_POST[ $card_field ] )
|
||||
: null;
|
||||
}
|
||||
|
||||
// Update a PaymentMethod.
|
||||
if ( 'pm_' === substr( $payment_method, 0, 3 ) ) {
|
||||
$address_args = array(
|
||||
'city' => $card_args['address_city'],
|
||||
'country' => $card_args['address_country'],
|
||||
'line1' => $card_args['address_line1'],
|
||||
'line2' => $card_args['address_line2'],
|
||||
'postal_code' => $card_args['address_zip'],
|
||||
'state' => $card_args['address_state'],
|
||||
);
|
||||
|
||||
edds_api_request(
|
||||
'PaymentMethod',
|
||||
'update',
|
||||
$payment_method,
|
||||
array(
|
||||
'billing_details' => array(
|
||||
'address' => $address_args,
|
||||
),
|
||||
'card' => array(
|
||||
'exp_month' => $card_args['exp_month'],
|
||||
'exp_year' => $card_args['exp_year'],
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Update a legacy Card.
|
||||
} else {
|
||||
edds_api_request( 'Customer', 'updateSource', $stripe_customer_id, $payment_method, $card_args );
|
||||
}
|
||||
|
||||
// Check if customer has default card.
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
$default_payment_method = edds_customer_get_default_payment_method( get_current_user_id(), $existing_cards );
|
||||
// If there is no default card, make updated card default.
|
||||
if ( null === $default_payment_method ) {
|
||||
edds_customer_set_default_payment_method( $stripe_customer_id, current( $existing_cards )['source']->id );
|
||||
}
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => esc_html__( 'Card successfully updated.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html( $e->getMessage() ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_update_payment_method', 'edd_stripe_process_card_update' );
|
||||
|
||||
/**
|
||||
* Process the set default card action from the manage card form.
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_process_card_default() {
|
||||
$enabled = edd_stripe_existing_cards_enabled();
|
||||
|
||||
// Feature not enabled.
|
||||
if ( ! $enabled ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'This feature is not available at this time.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Source can't be found.
|
||||
$payment_method = isset( $_POST['payment_method'] ) ? sanitize_text_field( $_POST['payment_method'] ) : '';
|
||||
|
||||
if ( empty( $payment_method ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Nonce failed.
|
||||
if ( ! edds_verify( 'nonce', $payment_method . '_update' ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Customer can't be found.
|
||||
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
|
||||
|
||||
if ( empty( $stripe_customer_id ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
edds_customer_set_default_payment_method( $stripe_customer_id, $payment_method );
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => esc_html__( 'Card successfully set as default.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html( $e->getMessage() ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_set_payment_method_default', 'edd_stripe_process_card_default' );
|
||||
|
||||
/**
|
||||
* Process the delete card action from the manage card form.
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_process_card_delete() {
|
||||
$enabled = edd_stripe_existing_cards_enabled();
|
||||
|
||||
// Feature not enabled.
|
||||
if ( ! $enabled ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'This feature is not available at this time.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Source can't be found.
|
||||
$payment_method = isset( $_POST['payment_method'] ) ? sanitize_text_field( $_POST['payment_method'] ) : '';
|
||||
|
||||
if ( empty( $payment_method ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Nonce failed.
|
||||
if ( ! edds_verify( 'nonce', $payment_method . '_update' ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Customer can't be found.
|
||||
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
|
||||
|
||||
if ( empty( $stripe_customer_id ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error updating card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Removal is disabled for this card.
|
||||
$should_remove = apply_filters(
|
||||
'edd_stripe_should_remove_card',
|
||||
array(
|
||||
'remove' => true,
|
||||
'message' => '',
|
||||
),
|
||||
$payment_method,
|
||||
$stripe_customer_id
|
||||
);
|
||||
|
||||
if ( ! $should_remove['remove'] ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html__( 'This feature is not available at this time.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Detach a PaymentMethod.
|
||||
if ( 'pm_' === substr( $payment_method, 0, 3 ) ) {
|
||||
$payment_method = edds_api_request( 'PaymentMethod', 'retrieve', $payment_method );
|
||||
$payment_method->detach();
|
||||
|
||||
// Delete a Card.
|
||||
} else {
|
||||
edds_api_request( 'Customer', 'deleteSource', $stripe_customer_id, $payment_method );
|
||||
}
|
||||
|
||||
// Retrieve saved cards before checking for default.
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
$default_payment_method = edds_customer_get_default_payment_method( get_current_user_id(), $existing_cards );
|
||||
|
||||
// If there is no default card, make updated card default.
|
||||
if ( null === $default_payment_method && ! empty( $existing_cards ) ) {
|
||||
edds_customer_set_default_payment_method( $stripe_customer_id, current( $existing_cards )['source']->id );
|
||||
}
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => esc_html__( 'Card successfully removed.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html( $e->getMessage() ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_delete_payment_method', 'edd_stripe_process_card_delete' );
|
||||
|
||||
/**
|
||||
* Handles adding a new PaymentMethod (via AJAX).
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edds_add_payment_method() {
|
||||
$enabled = edd_stripe_existing_cards_enabled();
|
||||
|
||||
// Feature not enabled.
|
||||
if ( ! $enabled ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'This feature is not available at this time.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
|
||||
// Increase the card error count.
|
||||
edd_stripe()->rate_limiting->increment_card_error_count();
|
||||
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Unable to update your account at this time, please try again later', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// PaymentMethod can't be found.
|
||||
$payment_method_id = isset( $_POST['payment_method_id'] ) ? sanitize_text_field( $_POST['payment_method_id'] ) : false;
|
||||
|
||||
if ( ! $payment_method_id ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Missing card ID.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Nonce failed.
|
||||
if ( ! edds_verify( 'nonce', 'edd-stripe-add-card' ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __( 'Error adding card.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$edd_customer = new \EDD_Customer( get_current_user_id(), true );
|
||||
if ( 0 === $edd_customer->id ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __(
|
||||
'Unable to retrieve customer.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
|
||||
$stripe_customer = edds_get_stripe_customer(
|
||||
$stripe_customer_id,
|
||||
array(
|
||||
'email' => $edd_customer->email,
|
||||
'description' => $edd_customer->email,
|
||||
)
|
||||
);
|
||||
if ( false === $stripe_customer ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => __(
|
||||
'Unable to create customer in Stripe.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure the EDD Customer is has a link to the most up to date Stripe Customer ID.
|
||||
$edd_customer->update_meta( edd_stripe_get_customer_key(), $stripe_customer->id );
|
||||
|
||||
try {
|
||||
$payment_method = edds_api_request( 'PaymentMethod', 'retrieve', $payment_method_id );
|
||||
$payment_method->attach(
|
||||
array(
|
||||
'customer' => $stripe_customer->id,
|
||||
)
|
||||
);
|
||||
// Check if customer has default card.
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
$default_payment_method = edds_customer_get_default_payment_method( get_current_user_id(), $existing_cards );
|
||||
// If there is no default card, make updated card default.
|
||||
if ( null === $default_payment_method ) {
|
||||
edds_customer_set_default_payment_method( $stripe_customer->id, current( $existing_cards )['source']->id );
|
||||
}
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => esc_html__( 'Card successfully added.', 'easy-digital-downloads' ),
|
||||
)
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
// Increase the card error count.
|
||||
edd_stripe()->rate_limiting->increment_card_error_count();
|
||||
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html( $e->getMessage() ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_add_payment_method', 'edds_add_payment_method' );
|
||||
|
||||
/**
|
||||
* Sets default payment method if none.
|
||||
*
|
||||
* @since 2.8
|
||||
* @param string $stripe_customer_id Stripe Customer ID. Usually starts with cus_ .
|
||||
* @param string $payment_method_id Stripe Payment ID. Usually starts with pm_ .
|
||||
* @return \Stripe\Customer $customer Stripe Customer.
|
||||
*/
|
||||
function edds_customer_set_default_payment_method( $stripe_customer_id, $payment_method_id ) {
|
||||
$customer = edds_api_request(
|
||||
'Customer',
|
||||
'update',
|
||||
$stripe_customer_id,
|
||||
array(
|
||||
'invoice_settings' => array(
|
||||
'default_payment_method' => $payment_method_id,
|
||||
),
|
||||
)
|
||||
);
|
||||
return $customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if customer has default payment method.
|
||||
*
|
||||
* @since 2.8
|
||||
* @param int $user_id WordPress user ID.
|
||||
* @param array $payment_methods Array of payment methods for user, default = false will fetch payment methods.
|
||||
* @return null|string Payment Method ID if found, else null
|
||||
*/
|
||||
function edds_customer_get_default_payment_method( $user_id, $payment_methods = false ) {
|
||||
// Retrieve saved cards before checking for default.
|
||||
if ( false === $payment_methods ) {
|
||||
$payment_methods = edd_stripe_get_existing_cards( $user_id );
|
||||
}
|
||||
$default_payment_method = null;
|
||||
if ( count( $payment_methods ) >= 1 ) {
|
||||
// Loop through existing cards for default.
|
||||
foreach ( $payment_methods as $card ) {
|
||||
if ( true === $card['default'] ) {
|
||||
$default_payment_method = $card['source']->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $default_payment_method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if customer Stripe Customer object exists.
|
||||
*
|
||||
* @since 2.8
|
||||
* @param string $stripe_customer_id Stripe Customer ID. Usually starts with cus_ .
|
||||
* @param array $customer_args {
|
||||
* Arguments to create a Stripe Customer.
|
||||
*
|
||||
* @link https://stripe.com/docs/api/customers/create
|
||||
* }
|
||||
* @return \Stripe\Customer|false $customer Stripe Customer if found or false on error.
|
||||
*/
|
||||
function edds_get_stripe_customer( $stripe_customer_id, $customer_args ) {
|
||||
$customer = false;
|
||||
if ( ! empty( $stripe_customer_id ) ) {
|
||||
try {
|
||||
$customer = edds_api_request( 'Customer', 'retrieve', $stripe_customer_id );
|
||||
if ( isset( $customer->deleted ) && $customer->deleted ) { // If customer was deleted in Stripe, try to create a new one.
|
||||
$customer = edds_create_stripe_customer( $customer_args );
|
||||
}
|
||||
} catch ( \Stripe\Error\Base $e ) {
|
||||
$error_code = $e->getStripeCode();
|
||||
if ( 'resource_missing' === $error_code ) { // If Stripe returns an error of 'resource_missing', try to create a new Stripe Customer.
|
||||
try {
|
||||
$customer = edds_create_stripe_customer( $customer_args );
|
||||
} catch ( \Exception $e ) {
|
||||
// No further actions to take if something causes error.
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$customer = edds_create_stripe_customer( $customer_args );
|
||||
} catch ( \Exception $e ) {
|
||||
// No further actions to take if something causes error.
|
||||
}
|
||||
}
|
||||
return $customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe Customer
|
||||
*
|
||||
* @since 2.8
|
||||
* @param array $customer_args {
|
||||
* Arguments to create a Stripe Customer.
|
||||
*
|
||||
* @link https://stripe.com/docs/api/customers/create
|
||||
* }
|
||||
* @return \Stripe\Customer|false $customer Stripe Customer if one is created or false on error.
|
||||
*/
|
||||
function edds_create_stripe_customer( $customer_args = array() ) {
|
||||
/**
|
||||
* Filters the arguments used to create a Customer in Stripe.
|
||||
*
|
||||
* @since unknown
|
||||
*
|
||||
* @param array $customer_args {
|
||||
* Arguments to create a Stripe Customer.
|
||||
*
|
||||
* @link https://stripe.com/docs/api/customers/create
|
||||
* }
|
||||
* @param array $purchase_data {
|
||||
* Cart purchase data if in the checkout context. Empty otherwise.
|
||||
* }
|
||||
*/
|
||||
$customer_args = apply_filters( 'edds_create_customer_args', $customer_args, array() );
|
||||
if ( empty( $customer_args['email'] ) || ! is_email( $customer_args['email'] ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( empty( $customer_args['description'] ) ) {
|
||||
$customer_args['description'] = $customer_args['email'];
|
||||
}
|
||||
|
||||
try {
|
||||
$customer = edds_api_request( 'Customer', 'create', $customer_args );
|
||||
} catch ( \Exception $e ) {
|
||||
$customer = false;
|
||||
}
|
||||
|
||||
return $customer;
|
||||
|
||||
}
|
@ -0,0 +1,423 @@
|
||||
<?php
|
||||
/**
|
||||
* EDD Stripe: Rate Limiting
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class EDD_Stripe_Rate_Limiting
|
||||
*/
|
||||
class EDD_Stripe_Rate_Limiting {
|
||||
|
||||
/**
|
||||
* Is the file writable.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $is_writable = true;
|
||||
|
||||
/**
|
||||
* The rate limiting log file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $filename = '';
|
||||
|
||||
/**
|
||||
* The file path to the log file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $file = '';
|
||||
|
||||
/**
|
||||
* Set up the EDD Logging Class
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->actions();
|
||||
$this->filters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any actions we need to use.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
private function actions() {
|
||||
|
||||
// Setup the log file.
|
||||
add_action( 'plugins_loaded', array( $this, 'setup_log_file' ), 11 );
|
||||
|
||||
// Maybe schedule the cron to clean up the log file.
|
||||
add_action( 'init', array( $this, 'schedule_cleanup' ) );
|
||||
|
||||
// Hook into the scheduled cleanup.
|
||||
add_action( 'edds_cleanup_rate_limiting_log', array( $this, 'cleanup_log' ) );
|
||||
|
||||
// Catch any recurring errors as they don't run through the main Stripe extension.
|
||||
add_action( 'edd_before_purchase_form', array( $this, 'listen_for_recurring_card_errors' ), 0 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any filters we need to use.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
private function filters() {
|
||||
|
||||
// Hide the purchase button if the visitor has hit the limit of errors.
|
||||
add_filter( 'edd_checkout_button_purchase', array( $this, 'maybe_hide_purchase_button' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a cleanup of the card testing log entries.
|
||||
*
|
||||
* Runs every hour, and clears any card testing logs that are past expiration.
|
||||
*
|
||||
* @since 2.8.13
|
||||
*/
|
||||
public function schedule_cleanup() {
|
||||
if ( ! wp_next_scheduled( 'edds_cleanup_rate_limiting_log' ) ) {
|
||||
wp_schedule_event( time(), 'hourly', 'edds_cleanup_rate_limiting_log' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the card testing logs.
|
||||
*
|
||||
* Loops over the card testing logs, and if an entry is past it's expiration, remove it from the list.
|
||||
*
|
||||
* @since 2.8.13
|
||||
*/
|
||||
public function cleanup_log() {
|
||||
$current_logs = $this->get_decoded_file();
|
||||
if ( empty( $current_logs ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $current_logs as $blocking_id => $entry ) {
|
||||
$expiration = ! empty( $entry['timeout'] ) ? $entry['timeout'] : 0;
|
||||
|
||||
if ( $expiration < current_time( 'timestamp' ) ) { // @codingStandardsIgnoreLine
|
||||
unset( $current_logs[ $blocking_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
$this->write_to_log( $current_logs );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the log file if it is writable
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @return void
|
||||
*/
|
||||
public function setup_log_file() {
|
||||
|
||||
$upload_dir = wp_upload_dir();
|
||||
$this->filename = wp_hash( home_url( '/' ) ) . '-edd-stripe-rate-limiting.log';
|
||||
$this->file = trailingslashit( $upload_dir['basedir'] ) . $this->filename;
|
||||
|
||||
if ( ! is_writeable( $upload_dir['basedir'] ) ) {
|
||||
$this->is_writable = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current session has hit the card error limit.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_hit_card_error_limit() {
|
||||
if ( ! $this->card_error_checks_enabled() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$blocking_id = $this->get_card_error_id();
|
||||
$entry = $this->get_rate_limiting_entry( $blocking_id );
|
||||
$expiration = ! empty( $entry['timeout'] ) ? $entry['timeout'] : 0;
|
||||
$card_errors = ! empty( $entry['count'] ) ? $entry['count'] : 0;
|
||||
|
||||
if ( $expiration < current_time( 'timestamp' ) ) { // @codingStandardsIgnoreLine
|
||||
$this->remove_log_entry( $this->get_card_error_id() );
|
||||
return false;
|
||||
}
|
||||
|
||||
$max_error_count = 5;
|
||||
|
||||
/**
|
||||
* Filters the number of times checkout errors can occur before blocking future attempts.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param bool $max_error_count The maximum failed checkouts before blocking future attempts. Default 5.
|
||||
*/
|
||||
$max_error_count = apply_filters( 'edds_max_card_error_count', $max_error_count );
|
||||
|
||||
return $max_error_count <= $card_errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an entry from the rate limiting log.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $blocking_id The blocking ID for the rate limiting.
|
||||
*/
|
||||
public function remove_log_entry( $blocking_id = '' ) {
|
||||
$current_logs = $this->get_decoded_file();
|
||||
unset( $current_logs[ $blocking_id ] );
|
||||
|
||||
$this->write_to_log( $current_logs );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific entry from the rate limiting log.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $blocking_id The blocking ID to get the entry for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_rate_limiting_entry( $blocking_id = '' ) {
|
||||
$current_logs = $this->get_decoded_file();
|
||||
$entry = array();
|
||||
|
||||
if ( array_key_exists( $blocking_id, $current_logs ) ) {
|
||||
$entry = $current_logs[ $blocking_id ];
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the number of times an IP address has generated card errors.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_card_error_count() {
|
||||
$blocking_id = $this->get_card_error_id();
|
||||
$count = 0;
|
||||
|
||||
$current_blocks = $this->get_decoded_file();
|
||||
if ( array_key_exists( $blocking_id, $current_blocks ) ) {
|
||||
$count = $current_blocks[ $blocking_id ]['count'];
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the Stripe card error counter.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function increment_card_error_count() {
|
||||
$current_count = $this->get_card_error_count();
|
||||
$blocking_id = $this->get_card_error_id();
|
||||
|
||||
if ( empty( $current_count ) ) {
|
||||
$current_count = 1;
|
||||
} else {
|
||||
$current_count++;
|
||||
}
|
||||
|
||||
$this->update_rate_limiting_count( $blocking_id, $current_count );
|
||||
|
||||
return absint( $current_count );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an entry in the rate limiting array.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $blocking_id The blocking ID.
|
||||
* @param int $current_count The count to update to.
|
||||
*/
|
||||
protected function update_rate_limiting_count( $blocking_id = '', $current_count = 0 ) {
|
||||
|
||||
$expiration_in_seconds = HOUR_IN_SECONDS;
|
||||
|
||||
/**
|
||||
* Filters the length of time before checkout card error counts are reset.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param int $expiration_in_seconds The length in seconds before card error counts are reset. Default 60.
|
||||
*/
|
||||
$expiration_in_seconds = apply_filters( 'edds_card_error_timeout', $expiration_in_seconds );
|
||||
|
||||
$current_log = $this->get_decoded_file();
|
||||
|
||||
$current_log[ $blocking_id ]['count'] = $current_count;
|
||||
$current_log[ $blocking_id ]['timeout'] = current_time( 'timestamp' ) + $expiration_in_seconds; // @codingStandardsIgnoreLine
|
||||
|
||||
$this->write_to_log( $current_log );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we should check for Stripe card errors and track them.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function card_error_checks_enabled() {
|
||||
$checks_enabled = true;
|
||||
|
||||
/**
|
||||
* Filters if card errors should be checked and tracked during checkout.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param bool $checks_enabled Enables or disables card error checking on checkout. Default true.
|
||||
*/
|
||||
$checks_enabled = apply_filters( 'edds_card_error_checking_enabled', true );
|
||||
|
||||
return true === $checks_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the card error tracking ID.
|
||||
*
|
||||
* ID is the IP address of the visitor. Prepends the value with `edds_card_errors_` for use with the transient system.
|
||||
* Uses IP tracking in an attempt to mitigate the amount of bogus WordPress user accounts being created.
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @since 2.8.13 Try and use the __stripe_sid cookie before relying on IP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_card_error_id() {
|
||||
return isset( $_COOKIE['__stripe_sid'] ) ? $_COOKIE['__stripe_sid'] : edd_get_ip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we should hide the purchase button.
|
||||
*
|
||||
* When someone has hit the card error limit, the purchase button is hidden.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $purchase_button_markup The markup for the purchase button.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function maybe_hide_purchase_button( $purchase_button_markup = '' ) {
|
||||
if ( $this->has_hit_card_error_limit() ) {
|
||||
$purchase_button_markup = '';
|
||||
}
|
||||
|
||||
return $purchase_button_markup;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* When the purchase form errors are displayed, see if any were related to Stripe failures and increase the card error
|
||||
* counter.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
public function listen_for_recurring_card_errors() {
|
||||
|
||||
// Get all of our EDD errors.
|
||||
$errors = edd_get_errors();
|
||||
|
||||
// If any of our errors are Stripe card errors from recurring, increment the card error counter.
|
||||
if ( isset( $errors['edd_recurring_stripe_error'] ) && ! empty( $errors['edd_recurring_stripe_error'] ) ) {
|
||||
$this->increment_card_error_count();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the log data
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @return string
|
||||
*/
|
||||
protected function get_file_contents() {
|
||||
return $this->get_file();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the decoded array of rate limiting from the log file.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_decoded_file() {
|
||||
$decoded_contents = json_decode( $this->get_file_contents(), true );
|
||||
if ( is_null( $decoded_contents ) ) {
|
||||
$decoded_contents = array();
|
||||
}
|
||||
|
||||
return (array) $decoded_contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the file data is written to
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @return string
|
||||
*/
|
||||
protected function get_file() {
|
||||
|
||||
$file = json_encode( array() );
|
||||
|
||||
if ( @file_exists( $this->file ) ) {
|
||||
|
||||
if ( ! is_writeable( $this->file ) ) {
|
||||
$this->is_writable = false;
|
||||
}
|
||||
|
||||
$file = @file_get_contents( $this->file );
|
||||
} else {
|
||||
@file_put_contents( $this->file, $file ); // @codingStandardsIgnoreLine
|
||||
@chmod( $this->file, 0664 ); // @codingStandardsIgnoreLine
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the log message
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param array $content The content of the rate limiting.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write_to_log( $content = array() ) {
|
||||
if ( count( $content ) > 200 ) {
|
||||
// Reduce the max number of identifiers to 200.
|
||||
$content = array_slice( $content, -200 );
|
||||
}
|
||||
|
||||
$content = json_encode( $content );
|
||||
|
||||
if ( $this->is_writable ) {
|
||||
@file_put_contents( $this->file, $content ); // @codingStandardsIgnoreLine
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
<?php
|
||||
/**
|
||||
* Main plugin class.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @copyright Copyright (c) 2021, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.1
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* EDD_Stripe class.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
class EDD_Stripe {
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @var EDD_Stripe
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Rate limiting component.
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @var EDD_Stripe_Rate_Limiting
|
||||
*/
|
||||
public $rate_limiting;
|
||||
|
||||
/**
|
||||
* Instantiates or returns the singleton instance.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*
|
||||
* @return EDD_Stripe
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof EDD_Stripe ) ) {
|
||||
self::$instance = new EDD_Stripe;
|
||||
self::$instance->includes();
|
||||
self::$instance->setup_classes();
|
||||
self::$instance->actions();
|
||||
self::$instance->filters();
|
||||
|
||||
if ( true === edds_is_pro() ) {
|
||||
if ( class_exists( '\\EDD\\Extensions\\ExtensionRegistry' ) ) {
|
||||
add_action( 'edd_extension_license_init', function( \EDD\Extensions\ExtensionRegistry $registry ) {
|
||||
$registry->addExtension(
|
||||
EDD_STRIPE_PLUGIN_FILE,
|
||||
'Stripe Pro Payment Gateway',
|
||||
167,
|
||||
EDD_STRIPE_VERSION,
|
||||
'stripe_license_key'
|
||||
);
|
||||
} );
|
||||
} elseif ( class_exists( 'EDD_License' ) ) {
|
||||
new EDD_License(
|
||||
EDD_STRIPE_PLUGIN_FILE,
|
||||
'Stripe Pro Payment Gateway',
|
||||
EDD_STRIPE_VERSION,
|
||||
'Easy Digital Downloads',
|
||||
'stripe_license_key',
|
||||
null,
|
||||
167
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes files.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
private function includes() {
|
||||
if ( ! class_exists( 'Stripe\Stripe' ) ) {
|
||||
require_once EDDS_PLUGIN_DIR . '/vendor/autoload.php';
|
||||
}
|
||||
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/class-stripe-api.php';
|
||||
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/utils/exceptions/class-stripe-api-unmet-requirements.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/utils/exceptions/class-attribute-not-found.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/utils/exceptions/class-stripe-object-not-found.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/utils/exceptions/class-gateway-exception.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/utils/interface-static-registry.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/utils/class-registry.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/utils/modal.php';
|
||||
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/functions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/deprecated.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/compat.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/i18n.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/emails.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-receipt.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/card-actions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/gateway-actions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/gateway-filters.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/webhooks.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/elements.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/scripts.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/template-functions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/class-edd-stripe-rate-limiting.php';
|
||||
|
||||
// Payment Methods.
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/index.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/buy-now/index.php';
|
||||
|
||||
if ( is_admin() ) {
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/class-notices-registry.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/class-notices.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/notices.php';
|
||||
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/admin-actions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/admin-filters.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/settings/stripe-connect.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/settings.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/upgrade-functions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/admin/reporting/class-stripe-reports.php';
|
||||
}
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/integrations/wp-cli.php';
|
||||
}
|
||||
|
||||
if ( defined( 'EDD_ALL_ACCESS_VER' ) && EDD_ALL_ACCESS_VER ) {
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/integrations/edd-all-access.php';
|
||||
}
|
||||
|
||||
if ( class_exists( 'EDD_Auto_Register' ) ) {
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/integrations/edd-auto-register.php';
|
||||
}
|
||||
|
||||
// Pro.
|
||||
$pro = EDDS_PLUGIN_DIR . '/includes/pro/index.php';
|
||||
|
||||
if ( file_exists( $pro ) ) {
|
||||
require_once $pro;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies various hooks.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
private function actions() {
|
||||
add_action( 'init', array( self::$instance, 'load_textdomain' ) );
|
||||
add_action( 'admin_init', array( self::$instance, 'database_upgrades' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies various filters.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
private function filters() {
|
||||
add_filter( 'edd_payment_gateways', array( self::$instance, 'register_gateway' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures core components.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
private function setup_classes() {
|
||||
$this->rate_limiting = new EDD_Stripe_Rate_Limiting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs database upgrades.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public function database_upgrades() {
|
||||
$did_upgrade = false;
|
||||
$version = get_option( 'edds_stripe_version' );
|
||||
|
||||
if ( ! $version || version_compare( $version, EDD_STRIPE_VERSION, '<' ) ) {
|
||||
$did_upgrade = true;
|
||||
|
||||
switch ( EDD_STRIPE_VERSION ) {
|
||||
case '2.5.8' :
|
||||
edd_update_option( 'stripe_checkout_remember', true );
|
||||
break;
|
||||
case '2.8.0':
|
||||
edd_update_option( 'stripe_allow_prepaid', true );
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( $did_upgrade ) {
|
||||
update_option( 'edds_stripe_version', EDD_STRIPE_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the plugin text domain.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public function load_textdomain() {
|
||||
// Set filter for language directory
|
||||
$lang_dir = EDDS_PLUGIN_DIR . '/languages/';
|
||||
|
||||
// Traditional WordPress plugin locale filter
|
||||
$locale = apply_filters( 'plugin_locale', get_locale(), 'edds' );
|
||||
$mofile = sprintf( '%1$s-%2$s.mo', 'edds', $locale );
|
||||
|
||||
// Setup paths to current locale file
|
||||
$mofile_local = $lang_dir . $mofile;
|
||||
$mofile_global = WP_LANG_DIR . '/edd-stripe/' . $mofile;
|
||||
|
||||
// Look in global /wp-content/languages/edd-stripe/ folder
|
||||
if( file_exists( $mofile_global ) ) {
|
||||
load_textdomain( 'edds', $mofile_global );
|
||||
|
||||
// Look in local /wp-content/plugins/edd-stripe/languages/ folder
|
||||
} elseif( file_exists( $mofile_local ) ) {
|
||||
load_textdomain( 'edds', $mofile_local );
|
||||
|
||||
} else {
|
||||
// Load the default language files
|
||||
load_plugin_textdomain( 'edds', false, $lang_dir );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the gateway.
|
||||
*
|
||||
* @param array $gateways Payment gateways.
|
||||
* @return array
|
||||
*/
|
||||
public function register_gateway( $gateways ) {
|
||||
// Format: ID => Name.
|
||||
$gateways['stripe'] = array(
|
||||
'admin_label' => 'Stripe',
|
||||
'checkout_label' => __( 'Credit Card', 'easy-digital-downloads' ),
|
||||
'supports' => array(
|
||||
'buy_now',
|
||||
),
|
||||
'icons' => array(
|
||||
'mastercard',
|
||||
'visa',
|
||||
'discover',
|
||||
'americanexpress',
|
||||
),
|
||||
);
|
||||
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* Manage the Stripe API PHP bindings usage.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
use \Stripe\Stripe;
|
||||
|
||||
/**
|
||||
* Implements a wrapper for the Stripe API PHP bindings.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
class EDD_Stripe_API {
|
||||
|
||||
/**
|
||||
* Configures the Stripe API before each request.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'edds_pre_stripe_api_request', array( $this, 'set_api_key' ) );
|
||||
add_action( 'edds_pre_stripe_api_request', array( $this, 'set_app_info' ) );
|
||||
add_action( 'edds_pre_stripe_api_request', array( $this, 'set_api_version' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an API request.
|
||||
*
|
||||
* Requires a Stripe object and method, with optional additional arguments.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @link https://github.com/stripe/stripe-php
|
||||
*
|
||||
* @throws \EDD_Stripe_Utils_Exceptions_Stripe_Object_Not_Found When attempting to call an object or method that is not available.
|
||||
* @throws \Stripe\Exception
|
||||
*
|
||||
* @param string $object Stripe object, such as Customer, Subscription, PaymentMethod, etc.
|
||||
* @param string $method Method to call on the object, such as update, retrieve, etc.
|
||||
* @param mixed ...$args Additional arguments to pass to the request.
|
||||
* @return \Stripe\StripeObject
|
||||
*/
|
||||
public function request( $object, $method ) {
|
||||
// Nothing should be executing API requests if the application requirements
|
||||
// have not been met. However, a final safety net is added here.
|
||||
if ( false === edds_has_met_requirements() ) {
|
||||
throw new EDD_Stripe_Utils_Exceptions_Stripe_API_Unmet_Requirements(
|
||||
__( 'Unable to process request: Unmet Stripe payment gateway requirements. Please contact the website administrator.', 'easy-digital-downloads' )
|
||||
);
|
||||
}
|
||||
|
||||
$classname = 'Stripe\\' . $object;
|
||||
|
||||
// Retrieve additional arguments.
|
||||
$args = func_get_args();
|
||||
unset( $args[0] ); // Removes $object.
|
||||
unset( $args[1] ); // Removes $method.
|
||||
|
||||
// Reset keys.
|
||||
$args = array_values( $args );
|
||||
|
||||
if ( ! is_callable( array( $classname, $method ) ) ) {
|
||||
throw new EDD_Stripe_Utils_Exceptions_Stripe_Object_Not_Found(
|
||||
sprintf(
|
||||
/* translators: %1$s Stripe API class name. %2$s Stripe API method name. */
|
||||
esc_html__( 'Unable to call %1$s::%2$s', 'easy-digital-downloads' ),
|
||||
$classname,
|
||||
$method
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows action to be taken before a Stripe API request is made.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param EDD_Stripe_API $this EDD_Stripe_API class.
|
||||
* @param string $object Stripe object, such as Customer, Subscription, PaymentMethod, etc.
|
||||
* @param string $method Method to call on the object, such as update, retrieve, etc.
|
||||
* @param mixed $args Additional arguments to pass to the request.
|
||||
*/
|
||||
do_action( 'edds_pre_stripe_api_request', $this, $object, $method, $args );
|
||||
|
||||
// @todo Filter arguments and per-request options?
|
||||
//
|
||||
// Need to account for:
|
||||
//
|
||||
// ::retrieve( array() );
|
||||
// ::retrieve( array(), array() );
|
||||
// ::retrieve( '123' );
|
||||
// ::retrieve( '123', array() );
|
||||
// ::update( '123', array() );
|
||||
// ::update( '123', array(), array() );
|
||||
|
||||
return call_user_func_array( array( $classname, $method ), $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets API key for all proceeding requests.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public function set_api_key() {
|
||||
$secret_key = edd_get_option( ( edd_is_test_mode() ? 'test' : 'live' ) . '_secret_key' );
|
||||
|
||||
Stripe::setApiKey( trim( $secret_key ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets application info for all proceeding requests.
|
||||
*
|
||||
* @link https://stripe.com/docs/building-plugins#setappinfo
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public function set_app_info() {
|
||||
Stripe::setAppInfo(
|
||||
'WordPress Easy Digital Downloads - Stripe',
|
||||
EDD_STRIPE_VERSION,
|
||||
esc_url( site_url() ),
|
||||
EDD_STRIPE_PARTNER_ID
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets API version for all proceeding requests.
|
||||
*
|
||||
* @link https://stripe.com/docs/building-plugins#set-api-version
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public function set_api_version() {
|
||||
Stripe::setApiVersion( EDD_STRIPE_API_VERSION );
|
||||
}
|
||||
}
|
@ -0,0 +1,441 @@
|
||||
<?php
|
||||
/**
|
||||
* Rewritten core functions to provide compatibility with a full AJAX checkout.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Maps serialized form data to global $_POST and $_REQUEST variables.
|
||||
*
|
||||
* This ensures any custom code that hooks in to actions inside an
|
||||
* AJAX processing step can utilize form field data.
|
||||
*
|
||||
* @since 2.7.3
|
||||
*
|
||||
* @param array $post_data $_POST data containing serialized form data.
|
||||
*/
|
||||
function _edds_map_form_data_to_request( $post_data ) {
|
||||
if ( ! isset( $post_data['form_data'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
parse_str( $post_data['form_data'], $form_data );
|
||||
|
||||
$_POST = array_merge( $_POST, $form_data );
|
||||
$_REQUEST = array_merge( $_REQUEST, $_POST );
|
||||
}
|
||||
|
||||
/**
|
||||
* When dealing with payments certain aspects only work if the payment
|
||||
* is being created inside the `edd_process_purchase_form()` function.
|
||||
*
|
||||
* Since this gateway uses multiple steps via AJAX requests this context gets lost.
|
||||
* Calling this function "fakes" that we are still in this process when creating
|
||||
* a new payment.
|
||||
*
|
||||
* Mainly this prevents `edd_insert_payment()` from creating multiple customers for
|
||||
* the same user by ensuring the checkout email address is added to the existing customer.
|
||||
*
|
||||
* @link https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/includes/payments/class-edd-payment.php#L2754
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
function _edds_fake_process_purchase_step() {
|
||||
// Save current errors.
|
||||
$errors = edd_get_errors();
|
||||
|
||||
// Clear any errors that might be used as a reason to attempt a redirect in the following action.
|
||||
edd_clear_errors();
|
||||
|
||||
// Don't run any attached actions twice.
|
||||
remove_all_actions( 'edd_pre_process_purchase' );
|
||||
|
||||
// Pretend we are about to process a purchase.
|
||||
do_action( 'edd_pre_process_purchase' );
|
||||
|
||||
// Clear any errors that may have been set in the previous action.
|
||||
edd_clear_errors();
|
||||
|
||||
// Restore original errors.
|
||||
if ( ! empty( $errors ) ) {
|
||||
foreach ( $errors as $error_id => $error_message ) {
|
||||
edd_set_error( $error_id, $error_message );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A rewritten version of `edds_get_purchase_form_user()` that can be run during AJAX.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function _edds_get_purchase_form_user( $valid_data = array() ) {
|
||||
// Initialize user
|
||||
$user = false;
|
||||
|
||||
if ( is_user_logged_in() ) {
|
||||
|
||||
// Set the valid user as the logged in collected data.
|
||||
$user = $valid_data['logged_in_user'];
|
||||
|
||||
} elseif ( true === $valid_data['need_new_user'] || true === $valid_data['need_user_login'] ) {
|
||||
|
||||
// Ensure $_COOKIE is available without a new HTTP request.
|
||||
add_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
|
||||
|
||||
// New user registration.
|
||||
if ( true === $valid_data['need_new_user'] ) {
|
||||
|
||||
// Set user.
|
||||
$user = $valid_data['new_user_data'];
|
||||
|
||||
// Register and login new user.
|
||||
$user['user_id'] = edd_register_and_login_new_user( $user );
|
||||
|
||||
} elseif ( true === $valid_data['need_user_login'] ) { // User login.
|
||||
|
||||
/*
|
||||
* The login form is now processed in the edd_process_purchase_login() function.
|
||||
* This is still here for backwards compatibility.
|
||||
* This also allows the old login process to still work if a user removes the
|
||||
* checkout login submit button.
|
||||
*
|
||||
* This also ensures that the customer is logged in correctly if they click "Purchase"
|
||||
* instead of submitting the login form, meaning the customer is logged in during the purchase process.
|
||||
*/
|
||||
|
||||
// Set user.
|
||||
$user = $valid_data['login_user_data'];
|
||||
|
||||
// Login user.
|
||||
if ( empty( $user ) || -1 === $user['user_id'] ) {
|
||||
edd_set_error( 'invalid_user', __( 'The user information is invalid', 'easy-digital-downloads' ) );
|
||||
return false;
|
||||
} else {
|
||||
edd_log_user_in( $user['user_id'], $user['user_login'], $user['user_pass'] );
|
||||
}
|
||||
}
|
||||
|
||||
remove_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
|
||||
}
|
||||
|
||||
// Check guest checkout.
|
||||
if ( false === $user && false === edd_no_guest_checkout() ) {
|
||||
// Set user.
|
||||
$user = $valid_data['guest_user_data'];
|
||||
}
|
||||
|
||||
// Verify we have an user.
|
||||
if ( false === $user || empty( $user ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get user first name.
|
||||
if ( ! isset( $user['user_first'] ) || strlen( trim( $user['user_first'] ) ) < 1 ) {
|
||||
$user['user_first'] = isset( $_POST["edd_first"] ) ? strip_tags( trim( $_POST["edd_first"] ) ) : '';
|
||||
}
|
||||
|
||||
// Get user last name.
|
||||
if ( ! isset( $user['user_last'] ) || strlen( trim( $user['user_last'] ) ) < 1 ) {
|
||||
$user['user_last'] = isset( $_POST["edd_last"] ) ? strip_tags( trim( $_POST["edd_last"] ) ) : '';
|
||||
}
|
||||
|
||||
// Get the user's billing address details.
|
||||
$user['address'] = array();
|
||||
$user['address']['line1'] = ! empty( $_POST['card_address'] ) ? sanitize_text_field( $_POST['card_address'] ) : '';
|
||||
$user['address']['line2'] = ! empty( $_POST['card_address_2'] ) ? sanitize_text_field( $_POST['card_address_2'] ) : '';
|
||||
$user['address']['city'] = ! empty( $_POST['card_city'] ) ? sanitize_text_field( $_POST['card_city'] ) : '';
|
||||
$user['address']['state'] = ! empty( $_POST['card_state'] ) ? sanitize_text_field( $_POST['card_state'] ) : '';
|
||||
$user['address']['country'] = ! empty( $_POST['billing_country'] ) ? sanitize_text_field( $_POST['billing_country'] ) : '';
|
||||
$user['address']['zip'] = ! empty( $_POST['card_zip'] ) ? sanitize_text_field( $_POST['card_zip'] ) : '';
|
||||
|
||||
if ( empty( $user['address']['country'] ) ) {
|
||||
$user['address'] = false; // Country will always be set if address fields are present.
|
||||
}
|
||||
|
||||
if ( ! empty( $user['user_id'] ) && $user['user_id'] > 0 && ! empty( $user['address'] ) ) {
|
||||
// EDD3: Add the customer address if it doesn't already exist.
|
||||
if ( function_exists( 'edd_maybe_add_customer_address' ) ) {
|
||||
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
|
||||
if ( $customer ) {
|
||||
edd_maybe_add_customer_address( $customer->id, $user['address'] );
|
||||
}
|
||||
} else {
|
||||
// Store the address in the user's meta so the cart can be pre-populated with it on return purchases.
|
||||
update_user_meta( $user['user_id'], '_edd_user_address', $user['address'] );
|
||||
}
|
||||
}
|
||||
|
||||
// Return valid user.
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* A rewritten version of `edd_process_purchase_form()` that allows for full AJAX processing.
|
||||
*
|
||||
* `edd_process_purchase_form()` is run up until:
|
||||
*
|
||||
* if ( $is_ajax ) {
|
||||
* echo 'success';
|
||||
* edd_die();
|
||||
* }
|
||||
*
|
||||
* Then this function is called which reruns the start of `edd_process_purchase_form()` and
|
||||
* continues the rest of the processing.
|
||||
*
|
||||
* @link https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/includes/process-purchase.php
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
function _edds_process_purchase_form() {
|
||||
// Catch exceptions at a high level.
|
||||
try {
|
||||
// `edd_process_purchase_form()` and subsequent code executions are written
|
||||
// expecting form processing to happen via a POST request from a client form.
|
||||
//
|
||||
// This version is called from an AJAX POST request, so the form data is sent
|
||||
// in a serialized string to ensure all fields are available.
|
||||
//
|
||||
// Map and merge formData to $_POST so it's accessible in other functions.
|
||||
parse_str( $_POST['form_data'], $form_data );
|
||||
$_POST = array_merge( $_POST, $form_data );
|
||||
$_REQUEST = array_merge( $_REQUEST, $_POST );
|
||||
|
||||
/*
|
||||
* Reset the tax rate so that it will be recalculated correctly.
|
||||
* This is only needed on EDD 3.0+.
|
||||
*/
|
||||
if ( method_exists( EDD()->cart, 'set_tax_rate' ) ) {
|
||||
EDD()->cart->set_tax_rate( null );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since unknown
|
||||
* @todo document
|
||||
*/
|
||||
do_action( 'edd_pre_process_purchase' );
|
||||
|
||||
// Make sure the cart isn't empty.
|
||||
if ( ! edd_get_cart_contents() && ! edd_cart_has_fees() ) {
|
||||
throw new \Exception( esc_html__( 'Your cart is empty.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
if ( ! isset( $_POST['edd-process-checkout-nonce'] ) ) {
|
||||
edd_debug_log( __( 'Missing nonce when processing checkout. Please read the following for more information: https://easydigitaldownloads.com/development/2018/07/05/important-update-to-ajax-requests-in-easy-digital-downloads-2-9-4', 'easy-digital-downloads' ), true );
|
||||
}
|
||||
|
||||
// Verify the checkout session only.
|
||||
if ( false === edds_verify() ) {
|
||||
throw new \Exception( esc_html__( 'Error processing purchase. Please reload the page and try again.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
// Validate the form $_POST data.
|
||||
$valid_data = edd_purchase_form_validate_fields();
|
||||
|
||||
// Allow themes and plugins to hook to errors.
|
||||
//
|
||||
// In the future these should throw exceptions, existing `edd_set_error()` usage will be caught below.
|
||||
do_action( 'edd_checkout_error_checks', $valid_data, $_POST );
|
||||
|
||||
// Validate the user.
|
||||
$user = _edds_get_purchase_form_user( $valid_data );
|
||||
|
||||
// Let extensions validate fields after user is logged in if user has used login/registration form
|
||||
do_action( 'edd_checkout_user_error_checks', $user, $valid_data, $_POST );
|
||||
|
||||
if ( false === $valid_data || edd_get_errors() || ! $user ) {
|
||||
$errors = edd_get_errors();
|
||||
|
||||
if ( is_array( $errors ) ) {
|
||||
throw new \Exception( current( $errors ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Setup user information.
|
||||
$user_info = array(
|
||||
'id' => $user['user_id'],
|
||||
'email' => $user['user_email'],
|
||||
'first_name' => $user['user_first'],
|
||||
'last_name' => $user['user_last'],
|
||||
'discount' => $valid_data['discount'],
|
||||
'address' => ! empty( $user['address'] ) ? $user['address'] : array(),
|
||||
);
|
||||
|
||||
// Update a customer record if they have added/updated information.
|
||||
$customer = new EDD_Customer( $user_info['email'] );
|
||||
|
||||
$name = $user_info['first_name'] . ' ' . $user_info['last_name'];
|
||||
|
||||
if ( empty( $customer->name ) || $name != $customer->name ) {
|
||||
$update_data = array(
|
||||
'name' => $name
|
||||
);
|
||||
|
||||
// Update the customer's name and update the user record too.
|
||||
$customer->update( $update_data );
|
||||
|
||||
wp_update_user( array(
|
||||
'ID' => get_current_user_id(),
|
||||
'first_name' => $user_info['first_name'],
|
||||
'last_name' => $user_info['last_name']
|
||||
) );
|
||||
}
|
||||
|
||||
// Update the customer's address if different to what's in the database
|
||||
$address = wp_parse_args( $user_info['address'], array(
|
||||
'line1' => '',
|
||||
'line2' => '',
|
||||
'city' => '',
|
||||
'state' => '',
|
||||
'country' => '',
|
||||
'zip' => '',
|
||||
) );
|
||||
|
||||
$address = array(
|
||||
'address' => $address['line1'],
|
||||
'address2' => $address['line2'],
|
||||
'city' => $address['city'],
|
||||
'region' => $address['state'],
|
||||
'country' => $address['country'],
|
||||
'postal_code' => $address['zip'],
|
||||
);
|
||||
|
||||
if ( ! empty( $user['user_id'] ) && $user['user_id'] > 0 && ! empty( $address ) ) {
|
||||
// EDD3: Add the customer address if it doesn't already exist.
|
||||
if ( function_exists( 'edd_maybe_add_customer_address' ) ) {
|
||||
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
|
||||
if ( $customer ) {
|
||||
edd_maybe_add_customer_address( $customer->id, $user['address'] );
|
||||
}
|
||||
} else {
|
||||
// Store the address in the user's meta so the cart can be pre-populated with it on return purchases.
|
||||
update_user_meta( $user['user_id'], '_edd_user_address', $user['address'] );
|
||||
}
|
||||
}
|
||||
|
||||
$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
|
||||
|
||||
$card_country = isset( $valid_data['cc_info']['card_country'] ) ? $valid_data['cc_info']['card_country'] : false;
|
||||
$card_state = isset( $valid_data['cc_info']['card_state'] ) ? $valid_data['cc_info']['card_state'] : false;
|
||||
$card_zip = isset( $valid_data['cc_info']['card_zip'] ) ? $valid_data['cc_info']['card_zip'] : false;
|
||||
|
||||
// Set up the unique purchase key. If we are resuming a payment, we'll overwrite this with the existing key.
|
||||
$purchase_key = strtolower( md5( $user['user_email'] . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'edd', true ) ) );
|
||||
$existing_payment = EDD()->session->get( 'edd_resume_payment' );
|
||||
|
||||
if ( ! empty( $existing_payment ) ) {
|
||||
$payment = new EDD_Payment( $existing_payment );
|
||||
|
||||
if( $payment->is_recoverable() && ! empty( $payment->key ) ) {
|
||||
$purchase_key = $payment->key;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup purchase information.
|
||||
$purchase_data = array(
|
||||
'downloads' => edd_get_cart_contents(),
|
||||
'fees' => edd_get_cart_fees(), // Any arbitrary fees that have been added to the cart
|
||||
'subtotal' => edd_get_cart_subtotal(), // Amount before taxes and discounts
|
||||
'discount' => edd_get_cart_discounted_amount(), // Discounted amount
|
||||
'tax' => edd_get_cart_tax(), // Taxed amount
|
||||
'tax_rate' => edd_use_taxes() ? edd_get_cart_tax_rate( $card_country, $card_state, $card_zip ) : 0, // Tax rate
|
||||
'price' => edd_get_cart_total(), // Amount after taxes
|
||||
'purchase_key' => $purchase_key,
|
||||
'user_email' => $user['user_email'],
|
||||
'date' => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
|
||||
'user_info' => stripslashes_deep( $user_info ),
|
||||
'post_data' => $_POST,
|
||||
'cart_details' => edd_get_cart_content_details(),
|
||||
'gateway' => $valid_data['gateway'],
|
||||
'card_info' => $valid_data['cc_info']
|
||||
);
|
||||
|
||||
// Add the user data for hooks
|
||||
$valid_data['user'] = $user;
|
||||
|
||||
// Allow themes and plugins to hook before the gateway
|
||||
do_action( 'edd_checkout_before_gateway', $_POST, $user_info, $valid_data );
|
||||
|
||||
// Store payment method data.
|
||||
$purchase_data['gateway_nonce'] = wp_create_nonce( 'edd-gateway' );
|
||||
|
||||
// Allow the purchase data to be modified before it is sent to the gateway
|
||||
$purchase_data = apply_filters(
|
||||
'edd_purchase_data_before_gateway',
|
||||
$purchase_data,
|
||||
$valid_data
|
||||
);
|
||||
|
||||
// Setup the data we're storing in the purchase session
|
||||
$session_data = $purchase_data;
|
||||
|
||||
// Used for showing download links to non logged-in users after purchase, and for other plugins needing purchase data.
|
||||
edd_set_purchase_session( $session_data );
|
||||
|
||||
/**
|
||||
* Allows further processing...
|
||||
*/
|
||||
do_action( 'edd_gateway_' . $purchase_data['gateway'], $purchase_data );
|
||||
} catch( \Exception $e ) {
|
||||
return wp_send_json_error( array(
|
||||
'message' => $e->getMessage(),
|
||||
) );
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_process_purchase_form', '_edds_process_purchase_form' );
|
||||
add_action( 'wp_ajax_nopriv_edds_process_purchase_form', '_edds_process_purchase_form' );
|
||||
|
||||
if ( ! function_exists( 'edd_is_dev_environment' ) ) {
|
||||
/**
|
||||
* Check the network site URL for signs of being a development environment.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return bool $retval True if dev, false if not.
|
||||
*/
|
||||
function edd_is_dev_environment() {
|
||||
|
||||
// Assume not a development environment
|
||||
$retval = false;
|
||||
|
||||
// Get this one time and use it below
|
||||
$network_url = network_site_url( '/' );
|
||||
|
||||
// Possible strings
|
||||
$strings = array(
|
||||
|
||||
// Popular port suffixes
|
||||
':8888', // This is common with MAMP on OS X
|
||||
|
||||
// Popular development TLDs
|
||||
'.dev', // VVV
|
||||
'.local', // Local
|
||||
'.test', // IETF
|
||||
'.example', // IETF
|
||||
'.invalid', // IETF
|
||||
'.localhost', // IETF
|
||||
|
||||
// Popular development subdomains
|
||||
'dev.',
|
||||
|
||||
// Popular development domains
|
||||
'localhost',
|
||||
'example.com',
|
||||
);
|
||||
|
||||
// Loop through all strings
|
||||
foreach ( $strings as $string ) {
|
||||
if ( stristr( $network_url, $string ) ) {
|
||||
$retval = $string;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter & return
|
||||
return apply_filters( 'edd_is_dev_environment', $retval );
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* Manage deprecations.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Process stripe checkout submission
|
||||
*
|
||||
* @access public
|
||||
* @since 1.0
|
||||
* @return void
|
||||
*/
|
||||
function edds_process_stripe_payment( $purchase_data ) {
|
||||
_edd_deprecated_function( 'edds_process_stripe_payment', '2.7.0', 'edds_process_purchase_form', debug_backtrace() );
|
||||
|
||||
return edds_process_purchase_form( $purchase_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Database Upgrade actions
|
||||
*
|
||||
* @access public
|
||||
* @since 2.5.8
|
||||
* @return void
|
||||
*/
|
||||
function edds_plugin_database_upgrades() {
|
||||
_edd_deprecated_function(
|
||||
__FUNCTION__,
|
||||
'2.8.1',
|
||||
null,
|
||||
debug_backtrace()
|
||||
);
|
||||
|
||||
edd_stripe()->database_upgrades();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internationalization
|
||||
*
|
||||
* @since 1.6.6
|
||||
* @return void
|
||||
*/
|
||||
function edds_textdomain() {
|
||||
_edd_deprecated_function(
|
||||
__FUNCTION__,
|
||||
'2.8.1',
|
||||
null,
|
||||
debug_backtrace()
|
||||
);
|
||||
|
||||
edd_stripe()->load_textdomain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register our payment gateway
|
||||
*
|
||||
* @since 1.0
|
||||
* @return array
|
||||
*/
|
||||
function edds_register_gateway( $gateways ) {
|
||||
_edd_deprecated_function(
|
||||
__FUNCTION__,
|
||||
'2.8.1',
|
||||
null,
|
||||
debug_backtrace()
|
||||
);
|
||||
|
||||
return edd_stripe()->register_gateway( $gateways );
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Stripe Elements functionality.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieves the styles passed to the Stripe Elements instance.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js
|
||||
* @link https://stripe.com/docs/stripe-js/reference#elements-create
|
||||
* @link https://stripe.com/docs/stripe-js/reference#element-options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function edds_get_stripe_elements_styles() {
|
||||
$elements_styles = array();
|
||||
|
||||
/**
|
||||
* Filters the styles used to create the Stripe Elements card field.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js/reference#element-options
|
||||
*
|
||||
* @param array $elements_styles Styles used to create Stripe Elements card field.
|
||||
*/
|
||||
$elements_styles = apply_filters( 'edds_stripe_elements_styles', $elements_styles );
|
||||
|
||||
return $elements_styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the options passed to the Stripe Elements instance.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js
|
||||
* @link https://stripe.com/docs/stripe-js/reference#elements-create
|
||||
* @link https://stripe.com/docs/stripe-js/reference#element-options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function edds_get_stripe_elements_options() {
|
||||
$elements_options = array(
|
||||
'hidePostalCode' => true,
|
||||
'i18n' => array(
|
||||
'errorMessages' => edds_get_localized_error_messages(),
|
||||
),
|
||||
);
|
||||
$elements_styles = edds_get_stripe_elements_styles();
|
||||
|
||||
if ( ! empty( $elements_styles ) ) {
|
||||
$elements_options['style'] = $elements_styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the options used to create the Stripe Elements card field.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js/reference#element-options
|
||||
*
|
||||
* @param array $elements_options Options used to create Stripe Elements card field.
|
||||
*/
|
||||
$elements_options = apply_filters( 'edds_stripe_elements_options', $elements_options );
|
||||
|
||||
// Elements doesn't like an empty array (which won't be converted to an object in JS).
|
||||
if ( empty( $elements_options ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $elements_options;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment emails.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Notify a customer that a Payment needs further action.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param int $payment_id EDD Payment ID.
|
||||
*/
|
||||
function edds_preapproved_payment_needs_action_notification( $payment_id ) {
|
||||
$payment = edd_get_payment( $payment_id );
|
||||
$payment_data = $payment->get_meta( '_edd_payment_meta', true );
|
||||
|
||||
$from_name = edd_get_option( 'from_name', wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ) );
|
||||
$from_name = apply_filters( 'edd_purchase_from_name', $from_name, $payment_id, $payment_data );
|
||||
$from_email = edd_get_option( 'from_email', get_bloginfo( 'admin_email' ) );
|
||||
$from_email = apply_filters( 'edd_purchase_from_address', $from_email, $payment_id, $payment_data );
|
||||
|
||||
if ( empty( $to_email ) ) {
|
||||
$to_email = $payment->email;
|
||||
}
|
||||
|
||||
$subject = esc_html__( 'Your Preapproved Payment Requires Action', 'easy-digital-downloads' );
|
||||
$heading = edd_do_email_tags( esc_html__( 'Payment Requires Action', 'easy-digital-downloads' ), $payment_id );
|
||||
|
||||
$message = esc_html__( 'Dear {name},', 'easy-digital-downloads' ) . "\n\n";
|
||||
$message .= esc_html__( 'Your preapproved payment requires further action before your purchase can be completed. Please click the link below to take finalize your purchase', 'easy-digital-downloads' ) . "\n\n";
|
||||
$message .= esc_url( add_query_arg( 'payment_key', $payment->key, edd_get_success_page_uri() ) );
|
||||
$message = edd_do_email_tags( $message, $payment_id );
|
||||
|
||||
/** This filter is documented in easy-digital-downloads/includes/emails/template.php */
|
||||
$message = apply_filters( 'edd_email_template_wpautop', true ) ? wpautop( $message ) : $message;
|
||||
|
||||
$emails = EDD()->emails;
|
||||
|
||||
$emails->__set( 'from_name', $from_name );
|
||||
$emails->__set( 'from_email', $from_email );
|
||||
$emails->__set( 'heading', $heading );
|
||||
|
||||
$headers = $emails->get_headers();
|
||||
$emails->__set( 'headers', $headers );
|
||||
|
||||
$emails->send( $to_email, $subject, $message );
|
||||
}
|
||||
add_action( 'edds_preapproved_payment_needs_action', 'edds_preapproved_payment_needs_action_notification' );
|
@ -0,0 +1,774 @@
|
||||
<?php
|
||||
/**
|
||||
* Functions
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since unknown
|
||||
*/
|
||||
|
||||
use EDD\Orders\Order;
|
||||
|
||||
/**
|
||||
* Returns the one true instance of EDD_Stripe
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void|\EDD_Stripe EDD_Stripe instance or void if Easy Digital
|
||||
* Downloads is not active.
|
||||
*/
|
||||
function edd_stripe() {
|
||||
// Easy Digital Downloads is not active, do nothing.
|
||||
if ( ! function_exists( 'EDD' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
return EDD_Stripe::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current execution of Stripe is being loaded from the
|
||||
* "Pro" version of the gateway.
|
||||
*
|
||||
* (Currently) when Stripe is packaged with EDD core the bootstrap file is
|
||||
* renamed to `edds_stripe_bootstrap_core()` to prevent collision.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @return bool True if the "Pro" version of the gateway is loaded.
|
||||
*/
|
||||
function edds_is_pro() {
|
||||
return function_exists( 'edd_stripe_bootstrap' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the Stripe gateway is active.
|
||||
*
|
||||
* This checks both application requirements being met as
|
||||
* well as the gateway being enabled via Payment Gateways settings.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function edds_is_gateway_active() {
|
||||
if ( false === edds_has_met_requirements() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( false === edd_is_gateway_active( 'stripe' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines of the application requirements have been met.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @param false|string $requirement Specific requirement to check for.
|
||||
* False ensures all requirements are met.
|
||||
* Default false.
|
||||
* @return bool True if the requirement(s) is met.
|
||||
*/
|
||||
function edds_has_met_requirements( $requirement = false ) {
|
||||
$requirements = array(
|
||||
'php' => (
|
||||
version_compare( PHP_VERSION, '5.6.0', '>' )
|
||||
),
|
||||
'edd' => (
|
||||
defined( 'EDD_VERSION' )
|
||||
? version_compare( EDD_VERSION, '2.11', '>=' )
|
||||
: true
|
||||
),
|
||||
'recurring' => (
|
||||
defined( 'EDD_RECURRING_VERSION' )
|
||||
? version_compare( EDD_RECURRING_VERSION, '2.9.99', '>' )
|
||||
: true
|
||||
),
|
||||
'wp' => (
|
||||
version_compare( get_bloginfo( 'version' ), '4.4', '>=' )
|
||||
),
|
||||
);
|
||||
|
||||
if ( false === $requirement ) {
|
||||
return false === in_array( false, $requirements, true );
|
||||
} else {
|
||||
return isset( $requirements[ $requirement ] )
|
||||
? $requirements[ $requirement ]
|
||||
: true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows preconfigured Stripe API requests to be made.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @throws \EDD_Stripe_Utils_Exceptions_Stripe_Object_Not_Found When attempting to call an object or method that is not available.
|
||||
* @throws \Stripe\Exception
|
||||
*
|
||||
* @param string $object Name of the Stripe object to request.
|
||||
* @param string $method Name of the API operation to perform during the request.
|
||||
* @param mixed ...$args Additional arguments to pass to the request.
|
||||
* @return \Stripe\StripeObject
|
||||
*/
|
||||
function edds_api_request( $object, $method, $args = null ) {
|
||||
$api = new EDD_Stripe_API();
|
||||
|
||||
return call_user_func_array( array( $api, 'request' ), func_get_args() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a truthy value (e.g. '1', 'yes') to a bool.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param mixed $truthy_value Truthy value.
|
||||
* @return bool
|
||||
*/
|
||||
function edds_truthy_to_bool( $truthy_value ) {
|
||||
$truthy = array(
|
||||
'yes',
|
||||
1,
|
||||
'1',
|
||||
'true',
|
||||
);
|
||||
|
||||
return is_bool( $truthy_value )
|
||||
? $truthy_value
|
||||
: in_array( strtolower( $truthy_value ), $truthy, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the exsting cards setting.
|
||||
* @return bool
|
||||
*/
|
||||
function edd_stripe_existing_cards_enabled() {
|
||||
$use_existing_cards = edd_get_option( 'stripe_use_existing_cards', false );
|
||||
return ! empty( $use_existing_cards );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a user ID, retrieve existing cards within stripe.
|
||||
*
|
||||
* @since 2.6
|
||||
* @param int $user_id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function edd_stripe_get_existing_cards( $user_id = 0 ) {
|
||||
if ( empty( $user_id ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$enabled = edd_stripe_existing_cards_enabled();
|
||||
|
||||
if ( ! $enabled ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
static $existing_cards;
|
||||
|
||||
if ( ! is_null( $existing_cards ) && array_key_exists( $user_id, $existing_cards ) ) {
|
||||
return $existing_cards[ $user_id ];
|
||||
}
|
||||
|
||||
// Check if the user has existing cards
|
||||
$customer_cards = array();
|
||||
$stripe_customer_id = edds_get_stripe_customer_id( $user_id );
|
||||
|
||||
if ( ! empty( $stripe_customer_id ) ) {
|
||||
try {
|
||||
$stripe_customer = edds_api_request( 'Customer', 'retrieve', $stripe_customer_id );
|
||||
|
||||
if ( isset( $stripe_customer->deleted ) && $stripe_customer->deleted ) {
|
||||
return $customer_cards;
|
||||
}
|
||||
|
||||
$payment_methods = edds_api_request( 'PaymentMethod', 'all', array(
|
||||
'type' => 'card',
|
||||
'customer' => $stripe_customer->id,
|
||||
'limit' => 100,
|
||||
) );
|
||||
|
||||
$cards = edds_api_request( 'Customer', 'allSources', $stripe_customer->id, array(
|
||||
'limit' => 100,
|
||||
) );
|
||||
|
||||
$sources = array_merge( $payment_methods->data, $cards->data );
|
||||
|
||||
foreach ( $sources as $source ) {
|
||||
if ( ! in_array( $source->object, array( 'payment_method', 'card' ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source_data = new stdClass();
|
||||
$source_data->id = $source->id;
|
||||
|
||||
switch( $source->object ) {
|
||||
case 'payment_method':
|
||||
$source_data->brand = ucwords( $source->card->brand );
|
||||
$source_data->last4 = $source->card->last4;
|
||||
$source_data->exp_month = $source->card->exp_month;
|
||||
$source_data->exp_year = $source->card->exp_year;
|
||||
$source_data->fingerprint = $source->card->fingerprint;
|
||||
$source_data->address_line1 = $source->billing_details->address->line1;
|
||||
$source_data->address_line2 = $source->billing_details->address->line2;
|
||||
$source_data->address_city = $source->billing_details->address->city;
|
||||
$source_data->address_zip = $source->billing_details->address->postal_code;
|
||||
$source_data->address_state = $source->billing_details->address->state;
|
||||
$source_data->address_country = $source->billing_details->address->country;
|
||||
|
||||
$customer_cards[ $source->id ]['default'] = $source->id === $stripe_customer->invoice_settings->default_payment_method;
|
||||
break;
|
||||
case 'card':
|
||||
$source_data->brand = $source->brand;
|
||||
$source_data->last4 = $source->last4;
|
||||
$source_data->exp_month = $source->exp_month;
|
||||
$source_data->exp_year = $source->exp_year;
|
||||
$source_data->fingerprint = $source->fingerprint;
|
||||
$source_data->address_line1 = $source->address_line1;
|
||||
$source_data->address_line2 = $source->address_line2;
|
||||
$source_data->address_city = $source->address_city;
|
||||
$source_data->address_zip = $source->address_zip;
|
||||
$source_data->address_state = $source->address_state;
|
||||
$source_data->address_country = $source->address_country;
|
||||
break;
|
||||
}
|
||||
|
||||
$customer_cards[ $source->id ]['source'] = $source_data;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
return $customer_cards;
|
||||
}
|
||||
}
|
||||
|
||||
// Show only the latest version of card for duplicates.
|
||||
$fingerprints = array();
|
||||
foreach ( $customer_cards as $key => $customer_card ) {
|
||||
$fingerprint = $customer_card['source']->fingerprint;
|
||||
if ( ! in_array( $fingerprint, $fingerprints ) ) {
|
||||
$fingerprints[] = $fingerprint;
|
||||
} else {
|
||||
unset( $customer_cards[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
// Put default card first.
|
||||
usort(
|
||||
$customer_cards,
|
||||
function( $a, $b ) {
|
||||
return $a['default'] ? 1 : -1;
|
||||
}
|
||||
);
|
||||
|
||||
$existing_cards[ $user_id ] = $customer_cards;
|
||||
|
||||
return $existing_cards[ $user_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the stripe customer id in user meta, and look to recurring if not found yet
|
||||
*
|
||||
* @since 2.4.4
|
||||
* @since 2.6 Added lazy load for moving to customer meta and ability to look up by customer ID.
|
||||
* @param int $id_or_email The user ID, customer ID or email to look up.
|
||||
* @param bool $by_user_id If the lookup is by user ID or not.
|
||||
*
|
||||
* @return string Stripe customer ID
|
||||
*/
|
||||
function edds_get_stripe_customer_id( $id_or_email, $by_user_id = true ) {
|
||||
$stripe_customer_id = '';
|
||||
$meta_key = edd_stripe_get_customer_key();
|
||||
|
||||
if ( is_email( $id_or_email ) ) {
|
||||
$by_user_id = false;
|
||||
}
|
||||
|
||||
$customer = new EDD_Customer( $id_or_email, $by_user_id );
|
||||
if ( $customer->id > 0 ) {
|
||||
$stripe_customer_id = $customer->get_meta( $meta_key );
|
||||
}
|
||||
|
||||
if ( empty( $stripe_customer_id ) ) {
|
||||
$user_id = 0;
|
||||
if ( ! empty( $customer->user_id ) ) {
|
||||
$user_id = $customer->user_id;
|
||||
} else if ( $by_user_id && is_numeric( $id_or_email ) ) {
|
||||
$user_id = $id_or_email;
|
||||
} else if ( is_email( $id_or_email ) ) {
|
||||
$user = get_user_by( 'email', $id_or_email );
|
||||
if ( $user ) {
|
||||
$user_id = $user->ID;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $user ) ) {
|
||||
$user = get_user_by( 'id', $user_id );
|
||||
}
|
||||
|
||||
if ( $user ) {
|
||||
|
||||
$customer = new EDD_Customer( $user->user_email );
|
||||
|
||||
if ( ! empty( $user_id ) ) {
|
||||
$stripe_customer_id = get_user_meta( $user_id, $meta_key, true );
|
||||
|
||||
// Lazy load migrating data over to the customer meta from Stripe issue #113
|
||||
$customer->update_meta( $meta_key, $stripe_customer_id );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( empty( $stripe_customer_id ) && class_exists( 'EDD_Recurring_Subscriber' ) ) {
|
||||
|
||||
$subscriber = new EDD_Recurring_Subscriber( $id_or_email, $by_user_id );
|
||||
|
||||
if ( $subscriber->id > 0 ) {
|
||||
|
||||
$verified = false;
|
||||
|
||||
if ( ( $by_user_id && $id_or_email == $subscriber->user_id ) ) {
|
||||
// If the user ID given, matches that of the subscriber
|
||||
$verified = true;
|
||||
} else {
|
||||
// If the email used is the same as the primary email
|
||||
if ( $subscriber->email == $id_or_email ) {
|
||||
$verified = true;
|
||||
}
|
||||
|
||||
// If the email is in the EDD 2.6 Additional Emails
|
||||
if ( property_exists( $subscriber, 'emails' ) && in_array( $id_or_email, $subscriber->emails ) ) {
|
||||
$verified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $verified ) {
|
||||
$stripe_customer_id = $subscriber->get_recurring_customer_id( 'stripe' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( ! empty( $stripe_customer_id ) ) {
|
||||
$customer->update_meta( $meta_key, $stripe_customer_id );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $stripe_customer_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta key for storing Stripe customer IDs in
|
||||
*
|
||||
* @access public
|
||||
* @since 1.6.7
|
||||
* @return string
|
||||
*/
|
||||
function edd_stripe_get_customer_key() {
|
||||
|
||||
$key = '_edd_stripe_customer_id';
|
||||
if( edd_is_test_mode() ) {
|
||||
$key .= '_test';
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the provided currency is a zero-decimal currency
|
||||
*
|
||||
* @access public
|
||||
* @since 1.8.4
|
||||
* @param string $currency Three-letter ISO currency code or an empty string. If empty, the shop's currency is used.
|
||||
* @since 2.8.8 $currency parameter added
|
||||
* @return bool
|
||||
*/
|
||||
function edds_is_zero_decimal_currency( $currency = '' ) {
|
||||
if ( empty( $currency ) ) {
|
||||
$currency = edd_get_currency();
|
||||
}
|
||||
|
||||
$currency = strtolower( $currency );
|
||||
|
||||
$currencies = array(
|
||||
'bif',
|
||||
'clp',
|
||||
'djf',
|
||||
'gnf',
|
||||
'jpy',
|
||||
'kmf',
|
||||
'krw',
|
||||
'mga',
|
||||
'pyg',
|
||||
'rwf',
|
||||
'ugx',
|
||||
'vnd',
|
||||
'vuv',
|
||||
'xaf',
|
||||
'xof',
|
||||
'xpf',
|
||||
);
|
||||
|
||||
return in_array( $currency, $currencies, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a sanitized statement descriptor.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return string $statement_descriptor Sanitized statement descriptor.
|
||||
*/
|
||||
function edds_get_statement_descriptor() {
|
||||
$statement_descriptor = edd_get_option( 'stripe_statement_descriptor', '' );
|
||||
$statement_descriptor = edds_sanitize_statement_descriptor( $statement_descriptor );
|
||||
|
||||
return $statement_descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of unsupported characters for Stripe statement descriptors.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return array $unsupported_characters List of unsupported characters.
|
||||
*/
|
||||
function edds_get_statement_descriptor_unsupported_characters() {
|
||||
$unsupported_characters = array(
|
||||
'<',
|
||||
'>',
|
||||
'"',
|
||||
'\'',
|
||||
'\\',
|
||||
'*',
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the list of unsupported characters for Stripe statement descriptors.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param array $unsupported_characters List of unsupported characters.
|
||||
*/
|
||||
$unsupported_characters = apply_filters( 'edds_get_statement_descriptor_unsupported_characters', $unsupported_characters );
|
||||
|
||||
return $unsupported_characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a string to be used for a statement descriptor.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @link https://stripe.com/docs/connect/statement-descriptors#requirements
|
||||
*
|
||||
* @param string $statement_descriptor Statement descriptor to sanitize.
|
||||
* @return string $statement_descriptor Sanitized statement descriptor.
|
||||
*/
|
||||
function edds_sanitize_statement_descriptor( $statement_descriptor ) {
|
||||
$unsupported_characters = edds_get_statement_descriptor_unsupported_characters();
|
||||
|
||||
$statement_descriptor = trim( str_replace( $unsupported_characters, '', $statement_descriptor ) );
|
||||
$statement_descriptor = substr( $statement_descriptor, 0, 22 );
|
||||
|
||||
return $statement_descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a given registry instance by name.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $name Registry name.
|
||||
* @return null|EDD_Stripe_Registry Null if the registry doesn't exist, otherwise the object instance.
|
||||
*/
|
||||
function edds_get_registry( $name ) {
|
||||
switch( $name ) {
|
||||
case 'admin-notices':
|
||||
$registry = EDD_Stripe_Admin_Notices_Registry::instance();
|
||||
break;
|
||||
default:
|
||||
$registry = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to verify a nonce from various payment forms when the origin
|
||||
* of the action isn't explicitly known.
|
||||
*
|
||||
* This could be coming from the Checkout, Payment Authorization,
|
||||
* or Update Payment Method (Recurring) form.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function edds_verify_payment_form_nonce() {
|
||||
if ( false !== edds_verify() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update Payment Method.
|
||||
$nonce = isset( $_POST['edd_recurring_update_nonce'] )
|
||||
? sanitize_text_field( $_POST['edd_recurring_update_nonce'] )
|
||||
: '';
|
||||
|
||||
if ( ! empty( $nonce ) ) {
|
||||
return wp_verify_nonce( $nonce, 'update-payment' );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to verify a given token or nonce.
|
||||
*
|
||||
* @since 2.8.12
|
||||
* @param string $nonce The nonce to check for (default is checkout).
|
||||
* @param string $action The nonce action (default is checkout).
|
||||
* @return bool
|
||||
*/
|
||||
function edds_verify( $nonce = 'edd-process-checkout-nonce', $action = 'edd-process-checkout' ) {
|
||||
// Tokenizer.
|
||||
$token = isset( $_POST['token'] ) ? sanitize_text_field( $_POST['token'] ) : '';
|
||||
$timestamp = isset( $_POST['timestamp'] ) ? sanitize_text_field( $_POST['timestamp'] ) : '';
|
||||
if ( ! empty( $timestamp ) && ! empty( $token ) ) {
|
||||
edd_debug_log( $action . ' verified by tokenizer: ' . $token );
|
||||
|
||||
return \EDD\Utils\Tokenizer::is_token_valid( $token, $timestamp );
|
||||
}
|
||||
|
||||
edd_debug_log( 'Attempted to verify ' . $action . ' session by tokenizer but the data was missing.' );
|
||||
|
||||
// Checkout.
|
||||
$nonce = isset( $_POST[ $nonce ] )
|
||||
? sanitize_text_field( $_POST[ $nonce ] )
|
||||
: '';
|
||||
|
||||
if ( empty( $nonce ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return wp_verify_nonce( $nonce, $action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes user to correct support documentation, depending on whether they are using Standard or Pro version of Stripe
|
||||
*
|
||||
* @since 2.8.1
|
||||
* @param string $type The type of Stripe documentation.
|
||||
* @return string
|
||||
*/
|
||||
function edds_documentation_route( $type ) {
|
||||
$base_url = 'https://docs.easydigitaldownloads.com/standard';
|
||||
|
||||
/**
|
||||
* Filter to change EDD-Stripe support url.
|
||||
*
|
||||
* @since 2.8.1
|
||||
* @param string $base_url The url string for EDD-Stripe standard.
|
||||
*/
|
||||
$base_url = apply_filters( 'edds_documentation_route_base', $base_url );
|
||||
|
||||
return trailingslashit( $base_url ) . $type;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current Stripe account's country supports application fees.
|
||||
*
|
||||
* @since 2.8.7
|
||||
*
|
||||
* @return bool True if the Stripe account country (or core "Base Country" setting)
|
||||
* can use the `application_fee_amount` parameter in API requests.
|
||||
* True if no country information can be found which will still be
|
||||
* validated by Stripe when a request is made.
|
||||
*/
|
||||
function edds_stripe_connect_account_country_supports_application_fees() {
|
||||
$edd_country = edd_get_option( 'base_country', '' );
|
||||
|
||||
$account_country = edd_get_option(
|
||||
'stripe_connect_account_country',
|
||||
$edd_country
|
||||
);
|
||||
|
||||
// If we have no country to compare against try to add an application fee.
|
||||
// If the Stripe account is actually one of the blocked countries an API
|
||||
// error will be reflected in the Checkout.
|
||||
if ( empty( $account_country ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$account_country = strtolower( $account_country );
|
||||
|
||||
$blocked_countries = array(
|
||||
'br',
|
||||
);
|
||||
|
||||
return ! in_array( $account_country, $blocked_countries, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Refunds a charge made via Stripe.
|
||||
*
|
||||
* @since 2.8.7
|
||||
*
|
||||
* @param int|Order $order_id_or_object The ID or object (EDD 3.0+ only) of the order to refund.
|
||||
* @param Order|null $refund_object Optional. The refund object associated with this
|
||||
* charge refund. If provided, then the refund amount
|
||||
* is used as the charge refund amount (for partial refunds), and
|
||||
* an EDD transaction record will be inserted (EDD 3.0+ only).
|
||||
*
|
||||
* @throws \EDD_Stripe_Utils_Exceptions_Stripe_Object_Not_Found When attempting to call an object or method that is not available.
|
||||
* @throws \Exception
|
||||
*/
|
||||
function edd_refund_stripe_purchase( $order_id_or_object, $refund_object = null ) {
|
||||
$order_id = $order_id_or_object instanceof Order ? $order_id_or_object->id : $order_id_or_object;
|
||||
|
||||
edd_debug_log( sprintf(
|
||||
'Processing Stripe refund for order #%d',
|
||||
$order_id
|
||||
) );
|
||||
|
||||
if ( ! is_numeric( $order_id ) ) {
|
||||
throw new \Exception( __( 'Invalid order ID.', 'easy-digital-downloads' ), 400 );
|
||||
}
|
||||
|
||||
$charge_id = edd_get_payment_transaction_id( $order_id );
|
||||
|
||||
if ( empty( $charge_id ) || $charge_id == $order_id ) {
|
||||
$notes = edd_get_payment_notes( $order_id );
|
||||
|
||||
foreach ( $notes as $note ) {
|
||||
if ( preg_match( '/^Stripe Charge ID: ([^\s]+)/', $note->comment_content, $match ) ) {
|
||||
$charge_id = $match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bail if no charge ID was found.
|
||||
if ( empty( $charge_id ) ) {
|
||||
edd_debug_log( sprintf( 'Exiting refund of order #%d. No Stripe charge found.', $order_id ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'charge' => $charge_id,
|
||||
);
|
||||
|
||||
if ( $refund_object instanceof Order && $order_id_or_object instanceof Order && abs( $refund_object->total ) !== abs( $order_id_or_object->total ) ) {
|
||||
$args['amount'] = abs( $refund_object->total );
|
||||
if ( ! edds_is_zero_decimal_currency() ) {
|
||||
$args['amount'] = round( $args['amount'] * 100, 0 );
|
||||
}
|
||||
|
||||
edd_debug_log( sprintf(
|
||||
'Processing partial Stripe refund for order #%d. Refund amount: %s; Amount sent to Stripe: %s',
|
||||
$order_id_or_object->id,
|
||||
edd_currency_filter( $refund_object->total, $refund_object->currency ),
|
||||
$args['amount']
|
||||
) );
|
||||
} else {
|
||||
edd_debug_log( sprintf( 'Processing full Stripe refund for order #%d.', $order_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the refund arguments sent to Stripe.
|
||||
*
|
||||
* @link https://stripe.com/docs/api/refunds/create
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
$args = apply_filters( 'edds_create_refund_args', $args );
|
||||
|
||||
/**
|
||||
* Filters the secondary refund arguments.
|
||||
*
|
||||
* @param array $sec_args
|
||||
*/
|
||||
$sec_args = apply_filters( 'edds_create_refund_secondary_args', array() );
|
||||
|
||||
$refund = edds_api_request( 'Refund', 'create', $args, $sec_args );
|
||||
|
||||
$amount_refunded = (float) $refund->amount;
|
||||
if ( ! edds_is_zero_decimal_currency() ) {
|
||||
$amount_refunded = round( $amount_refunded / 100, edd_currency_decimal_filter( 2, strtoupper( $refund->currency ) ) );
|
||||
}
|
||||
|
||||
$order_note = sprintf(
|
||||
/* translators: %1$s the amount refunded; %2$s Stripe Refund ID */
|
||||
__( '%1$s refunded in Stripe. Refund ID %2$s', 'easy-digital-downloads' ),
|
||||
edd_currency_filter( $amount_refunded, strtoupper( $refund->currency ) ),
|
||||
$refund->id
|
||||
);
|
||||
|
||||
edd_insert_payment_note( $order_id, $order_note );
|
||||
|
||||
// Add a negative transaction in EDD 3.0+.
|
||||
if ( $refund_object instanceof Order && function_exists( 'edd_add_order_transaction' ) ) {
|
||||
edd_add_order_transaction( array(
|
||||
'object_id' => $refund_object->id,
|
||||
'object_type' => 'order',
|
||||
'transaction_id' => sanitize_text_field( $refund->id ),
|
||||
'gateway' => 'stripe',
|
||||
'status' => 'complete',
|
||||
'total' => edd_negate_amount( $amount_refunded )
|
||||
) );
|
||||
|
||||
edd_add_note( array(
|
||||
'object_id' => $refund_object->id,
|
||||
'object_type' => 'order',
|
||||
'user_id' => is_admin() ? get_current_user_id() : 0,
|
||||
'content' => $order_note
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers after a refund has been processed.
|
||||
*
|
||||
* @param int $order_id ID of the order that was refunded.
|
||||
*/
|
||||
do_action( 'edds_payment_refunded', $order_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Stripe preapproval is enabled. Pro must be active.
|
||||
*
|
||||
* @since 2.8.9
|
||||
* @return bool
|
||||
*/
|
||||
function edds_is_preapprove_enabled() {
|
||||
return edds_is_pro() && edd_get_option( 'stripe_preapprove_only' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hidden input for adding tokenizer support to Stripe checkout.
|
||||
*
|
||||
* @since 2.8.12
|
||||
* @param string $custom_id Optional: append a custom string to the ID.
|
||||
* @return string
|
||||
*/
|
||||
function edds_get_tokenizer_input( $custom_id = '' ) {
|
||||
|
||||
$id = 'edd-process-stripe-token';
|
||||
if ( $custom_id ) {
|
||||
$id .= '-' . $custom_id;
|
||||
}
|
||||
$timestamp = time();
|
||||
|
||||
return sprintf(
|
||||
'<input type="hidden" id="%s" data-timestamp="%s" data-token="%s" />',
|
||||
esc_attr( $id ),
|
||||
esc_attr( $timestamp ),
|
||||
esc_attr( \EDD\Utils\Tokenizer::tokenize( $timestamp ) )
|
||||
);
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Register payment statuses for preapproval
|
||||
*
|
||||
* @since 1.6
|
||||
* @return void
|
||||
*/
|
||||
function edds_register_post_statuses() {
|
||||
register_post_status( 'preapproval_pending', array(
|
||||
'label' => _x( 'Preapproval Pending', 'Pending preapproved payment', 'easy-digital-downloads' ),
|
||||
'public' => true,
|
||||
'exclude_from_search' => false,
|
||||
'show_in_admin_all_list' => true,
|
||||
'show_in_admin_status_list' => true,
|
||||
/* translators: %s Payment status count */
|
||||
'label_count' => _n_noop( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', 'easy-digital-downloads' )
|
||||
) );
|
||||
register_post_status( 'preapproval', array(
|
||||
'label' => _x( 'Preapproved', 'Preapproved payment', 'easy-digital-downloads' ),
|
||||
'public' => true,
|
||||
'exclude_from_search' => false,
|
||||
'show_in_admin_all_list' => true,
|
||||
'show_in_admin_status_list' => true,
|
||||
/* translators: %s Payment status count */
|
||||
'label_count' => _n_noop( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', 'easy-digital-downloads' )
|
||||
) );
|
||||
register_post_status( 'cancelled', array(
|
||||
'label' => _x( 'Cancelled', 'Cancelled payment', 'easy-digital-downloads' ),
|
||||
'public' => true,
|
||||
'exclude_from_search' => false,
|
||||
'show_in_admin_all_list' => true,
|
||||
'show_in_admin_status_list' => true,
|
||||
/* translators: %s Payment status count */
|
||||
'label_count' => _n_noop( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', 'easy-digital-downloads' )
|
||||
) );
|
||||
}
|
||||
add_action( 'init', 'edds_register_post_statuses', 110 );
|
||||
|
||||
/**
|
||||
* Register the statement_descriptor email tag.
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_register_email_tags() {
|
||||
$statement_descriptor = edds_get_statement_descriptor();
|
||||
if ( ! empty( $statement_descriptor ) ) {
|
||||
edd_add_email_tag(
|
||||
'stripe_statement_descriptor',
|
||||
__( 'Outputs a line stating what charges will appear as on customer\'s credit card statements.', 'easy-digital-downloads' ),
|
||||
'edd_stripe_statement_descriptor_template_tag',
|
||||
__( 'Statement Descriptor', 'easy-digital-downloads' )
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'edd_add_email_tags', 'edd_stripe_register_email_tags' );
|
||||
|
||||
/**
|
||||
* Swap the {statement_descriptor} email tag with the string from the option
|
||||
*
|
||||
* @since 2.6
|
||||
* @param $payment_id
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function edd_stripe_statement_descriptor_template_tag( $payment_id ) {
|
||||
$payment = new EDD_Payment( $payment_id );
|
||||
if ( 'stripe' !== $payment->gateway ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$statement_descriptor = edds_get_statement_descriptor();
|
||||
if ( empty( $statement_descriptor ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// If you want to filter this, use the %s to define where you want the actual statement descriptor to show in your message.
|
||||
$email_tag_output = __( apply_filters( 'edd_stripe_statement_descriptor_email_tag', 'Charges will appear on your card statement as %s' ), 'easy-digital-downloads' );
|
||||
|
||||
return sprintf( $email_tag_output, $statement_descriptor );
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Removes Stripe from active gateways if application requirements are not met.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @param array $enabled_gateways Enabled gateways that allow purchasing.
|
||||
* @return array
|
||||
*/
|
||||
function edds_validate_gateway_requirements( $enabled_gateways ) {
|
||||
if ( false === edds_has_met_requirements() ) {
|
||||
unset( $enabled_gateways['stripe'] );
|
||||
}
|
||||
|
||||
return $enabled_gateways;
|
||||
}
|
||||
add_filter( 'edd_enabled_payment_gateways', 'edds_validate_gateway_requirements', 20 );
|
||||
|
||||
/**
|
||||
* Injects the Stripe token and customer email into the pre-gateway data
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @param array $purchase_data
|
||||
* @return array
|
||||
*/
|
||||
function edd_stripe_straight_to_gateway_data( $purchase_data ) {
|
||||
|
||||
$gateways = edd_get_enabled_payment_gateways();
|
||||
|
||||
if ( isset( $gateways['stripe'] ) ) {
|
||||
$_REQUEST['edd-gateway'] = 'stripe';
|
||||
$purchase_data['gateway'] = 'stripe';
|
||||
}
|
||||
|
||||
return $purchase_data;
|
||||
}
|
||||
add_filter( 'edd_straight_to_gateway_purchase_data', 'edd_stripe_straight_to_gateway_data' );
|
||||
|
||||
/**
|
||||
* Process the POST Data for the Credit Card Form, if a token wasn't supplied
|
||||
*
|
||||
* @since 2.2
|
||||
* @return array The credit card data from the $_POST
|
||||
*/
|
||||
function edds_process_post_data( $purchase_data ) {
|
||||
if ( ! isset( $purchase_data['gateway'] ) || 'stripe' !== $purchase_data['gateway'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $_POST['edd_stripe_existing_card'] ) && 'new' !== $_POST['edd_stripe_existing_card'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Require a name for new cards.
|
||||
if ( ! isset( $_POST['card_name'] ) || strlen( trim( $_POST['card_name'] ) ) === 0 ) {
|
||||
edd_set_error( 'no_card_name', __( 'Please enter a name for the credit card.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
}
|
||||
add_action( 'edd_checkout_error_checks', 'edds_process_post_data' );
|
||||
|
||||
/**
|
||||
* Retrieves the locale used for Checkout modal window
|
||||
*
|
||||
* @since 2.5
|
||||
* @return string The locale to use
|
||||
*/
|
||||
function edds_get_stripe_checkout_locale() {
|
||||
return apply_filters( 'edd_stripe_checkout_locale', 'auto' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the $_COOKIE global when a logged in cookie is available.
|
||||
*
|
||||
* We need the global to be immediately available so calls to wp_create_nonce()
|
||||
* within the same session will use the newly available data.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @link https://wordpress.stackexchange.com/a/184055
|
||||
*
|
||||
* @param string $logged_in_cookie The logged-in cookie value.
|
||||
*/
|
||||
function edds_set_logged_in_cookie_global( $logged_in_cookie ) {
|
||||
$_COOKIE[ LOGGED_IN_COOKIE ] = $logged_in_cookie;
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* Internationalization
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @copyright Copyright (c) 2020, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of error codes and corresponding localized error messages.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return array $error_list List of error codes and corresponding error messages.
|
||||
*/
|
||||
function edds_get_localized_error_messages() {
|
||||
$generic_different_payment_method = __( 'There was an error processing your payment. Please try with a different payment method.', 'easy-digital-downloads' );
|
||||
$generic_contact_issuer_message = __( 'There was an error processing your payment. Please contact your card issuer for more information.', 'easy-digital-downloads' );
|
||||
|
||||
$error_list = array(
|
||||
'invalid_number' => __( 'The card number is not a valid credit card number.', 'easy-digital-downloads' ),
|
||||
'invalid_expiry_month' => __( 'The card\'s expiration month is invalid.', 'easy-digital-downloads' ),
|
||||
'invalid_expiry_year' => __( 'The card\'s expiration year is invalid.', 'easy-digital-downloads' ),
|
||||
'invalid_cvc' => __( 'The card\'s security code is invalid.', 'easy-digital-downloads' ),
|
||||
'incorrect_number' => __( 'The card number you provided is incorrect. Please check the number and try again.', 'easy-digital-downloads' ),
|
||||
'incomplete_number' => __( 'The card number is incomplete.', 'easy-digital-downloads' ),
|
||||
'incomplete_cvc' => __( 'The card\'s security code is incomplete.', 'easy-digital-downloads' ),
|
||||
'incomplete_expiry' => __( 'The card\'s expiration date is incomplete.', 'easy-digital-downloads' ),
|
||||
'incorrect_cvc' => __( 'The card\'s security code is incorrect.', 'easy-digital-downloads' ),
|
||||
'incorrect_zip' => __( 'The card\'s zip code failed validation.', 'easy-digital-downloads' ),
|
||||
'invalid_expiry_year_past' => __( 'The card\'s expiration year is in the past.', 'easy-digital-downloads' ),
|
||||
'processing_error' => __( 'An error occurred while processing the card. Please try again.', 'easy-digital-downloads' ),
|
||||
'email_invalid' => __( 'Invalid email address, please correct and try again.', 'easy-digital-downloads' ),
|
||||
'authentication_required' => __( 'Your purchase may require additional authentication. Please try again and confirm any authentication requests.', 'easy-digital-downloads' ),
|
||||
'approve_with_id' => __( 'There was an error processing your payment. Please try again, and if you continue to have problems, contact your card issuer.', 'easy-digital-downloads' ),
|
||||
'currency_not_supported' => __( 'Your payment method is not authorized to make purchases in this currency. Please contact your card issuer.', 'easy-digital-downloads' ),
|
||||
'expired_card' => __( 'The payment method you have provided is expired. Please try a different payment method.', 'easy-digital-downloads' ),
|
||||
'try_again_later' => __( 'There was an error processing your payment. Please try again.', 'easy-digital-downloads' ),
|
||||
'invalid_request_error' => $generic_different_payment_method,
|
||||
'insufficient_funds' => $generic_different_payment_method,
|
||||
'pin_try_exceeded' => $generic_different_payment_method,
|
||||
'withdrawal_count_limit_exceeded' => $generic_different_payment_method,
|
||||
'call_issuer' => $generic_contact_issuer_message,
|
||||
'card_not_supported' => $generic_contact_issuer_message,
|
||||
'card_velocity_exceeded' => $generic_contact_issuer_message,
|
||||
'do_not_honor' => $generic_contact_issuer_message,
|
||||
'do_not_try_again' => $generic_contact_issuer_message,
|
||||
'invalid_account' => $generic_contact_issuer_message,
|
||||
'invalid_amount' => $generic_contact_issuer_message,
|
||||
'issuer_not_available' => $generic_contact_issuer_message,
|
||||
'new_account_information_available' => $generic_contact_issuer_message,
|
||||
'no_action_taken' => $generic_contact_issuer_message,
|
||||
'not_permitted' => $generic_contact_issuer_message,
|
||||
'pickup_card' => $generic_contact_issuer_message,
|
||||
'restricted_card' => $generic_contact_issuer_message,
|
||||
'revocation_of_all_authorizations' => $generic_contact_issuer_message,
|
||||
'revocation_of_authorization' => $generic_contact_issuer_message,
|
||||
'security_violation' => $generic_contact_issuer_message,
|
||||
'service_not_allowed' => $generic_contact_issuer_message,
|
||||
'stop_payment_order' => $generic_contact_issuer_message,
|
||||
'transaction_not_allowed' => $generic_contact_issuer_message,
|
||||
'card_declined' => $generic_contact_issuer_message,
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the list of available error codes and corresponding error messages.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $error_list List of error codes and corresponding error messages.
|
||||
*/
|
||||
$error_list = apply_filters( 'edds_get_localized_error_list', $error_list );
|
||||
|
||||
return $error_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized error message for a corresponding Stripe
|
||||
* error code.
|
||||
*
|
||||
* @link https://stripe.com/docs/error-codes
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param string $error_code Error code.
|
||||
* @param string $error_message Original error message to return if a localized version does not exist.
|
||||
* @return string $error_message Potentially localized error message.
|
||||
*/
|
||||
function edds_get_localized_error_message( $error_code, $error_message, $decline_code = false ) {
|
||||
$error_list = edds_get_localized_error_messages();
|
||||
|
||||
if ( 'card_declined' === $error_code && false !== $decline_code && ! empty( $error_list[ $decline_code ] ) ) {
|
||||
$error_message = $error_list[ $decline_code ];
|
||||
} elseif ( ! empty( $error_list[ $error_code ] ) ) {
|
||||
$error_message = $error_list[ $error_code ];
|
||||
}
|
||||
|
||||
// To make development and testing easier for store owners, append the error and decline codes.
|
||||
if ( function_exists( 'wp_get_environment_type' ) && 'local' === wp_get_environment_type() ) {
|
||||
$error_message .= ' Error Code: ' . $error_code;
|
||||
if ( false !== $decline_code ) {
|
||||
$error_message .= ' Decline Code: ' . $decline_code;
|
||||
}
|
||||
}
|
||||
|
||||
return $error_message;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Integrations: All Access Pass
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Disables Payment Request Button output if Download has been unlocked with a pass.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param bool $enabled If the Payment Request Button is enabled.
|
||||
* @param int $download_id Current Download ID.
|
||||
* @return bool
|
||||
*/
|
||||
function edds_all_access_prb_purchase_link_enabled( $enabled, $download_id ) {
|
||||
$all_access = edd_all_access_check(
|
||||
array(
|
||||
'download_id' => $download_id,
|
||||
)
|
||||
);
|
||||
|
||||
return false === $all_access['success'];
|
||||
}
|
||||
add_filter( 'edds_prb_purchase_link_enabled', 'edds_all_access_prb_purchase_link_enabled', 10, 2 );
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Integration: Auto Register
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if the payment being created is by the Stripe gateway.
|
||||
* Ensures the registered user is logged in automatically if so.
|
||||
*
|
||||
* Added slightly early to not override anything more specific.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param bool $maybe_login Determines if the user should be logged in after registration.
|
||||
* @return bool
|
||||
*/
|
||||
function edds_auto_register_login_user( $maybe_login ) {
|
||||
|
||||
if ( false === edd_is_gateway_active( 'stripe' ) ) {
|
||||
return $maybe_login;
|
||||
}
|
||||
// If the request originated from the Stripe gateway on the Checkout, log in the registered user.
|
||||
if ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'edds_create_payment', 'edds_complete_payment' ), true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $maybe_login;
|
||||
}
|
||||
add_filter( 'edd_auto_register_login_user', 'edds_auto_register_login_user', 5 );
|
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Easy Digital Downloads WP-CLI Tools for Stripe
|
||||
*
|
||||
* This class provides an integration point with the WP-CLI plugin allowing
|
||||
* access to EDD from the command line.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Classes/CLI
|
||||
* @copyright Copyright (c) 2015, Chris Klosowski
|
||||
* @license http://opensource.org/license/gpl-2.0.php GNU Public License
|
||||
* @since 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
WP_CLI::add_command( 'edd-stripe', 'EDD_Stripe_CLI' );
|
||||
|
||||
/**
|
||||
* Work with EDD through WP-CLI
|
||||
*
|
||||
* EDD_CLI Class
|
||||
*
|
||||
* Adds CLI support to EDD through WP-CL
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class EDD_Stripe_CLI extends EDD_CLI {
|
||||
/**
|
||||
* Migrate the Stripe customer IDs from the usermeta table to the edd_customermeta table.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* --force=<boolean>: If the routine should be run even if the upgrade routine has been run already
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp edd-stripe migrate_customer_ids
|
||||
* wp edd-stripe migrate_customer_ids --force
|
||||
*/
|
||||
public function migrate_customer_ids( $args, $assoc_args ) {
|
||||
global $wpdb;
|
||||
$force = isset( $assoc_args['force'] ) ? true : false;
|
||||
|
||||
$upgrade_completed = edd_has_upgrade_completed( 'stripe_customer_id_migration' );
|
||||
|
||||
if ( ! $force && $upgrade_completed ) {
|
||||
WP_CLI::error( __( 'The Stripe customer ID migration has already been run. To do this anyway, use the --force argument.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
$sql = "SELECT user_id, meta_key, meta_value FROM $wpdb->usermeta WHERE meta_key IN ( '_edd_stripe_customer_id', '_edd_stripe_customer_id_test' )";
|
||||
$results = $wpdb->get_results( $sql );
|
||||
$total = count( $results );
|
||||
|
||||
if ( ! empty( $total ) ) {
|
||||
|
||||
$progress = new \cli\progress\Bar( 'Processing user meta', $total );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$user_data = get_userdata( $result->user_id );
|
||||
$customer = new EDD_Customer( $user_data->user_email );
|
||||
|
||||
if ( ! $customer->id > 0 ) {
|
||||
$customer = new EDD_Customer( $result->user_id, true );
|
||||
|
||||
if ( ! $customer->id > 0 ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$stripe_customer_id = $result->meta_value;
|
||||
|
||||
// We should try and use a recurring ID if one exists for this user
|
||||
if ( class_exists( 'EDD_Recurring_Subscriber' ) ) {
|
||||
$subscriber = new EDD_Recurring_Subscriber( $customer->id );
|
||||
$stripe_customer_id = $subscriber->get_recurring_customer_id( 'stripe' );
|
||||
}
|
||||
|
||||
$customer->update_meta( $result->meta_key, $stripe_customer_id );
|
||||
|
||||
$progress->tick();
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
WP_CLI::line( __( 'Migration complete.', 'easy-digital-downloads' ) );
|
||||
} else {
|
||||
WP_CLI::line( __( 'No user records were found that needed to be migrated.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
update_option( 'edds_stripe_version', preg_replace( '/[^0-9.].*/', '', EDD_STRIPE_VERSION ) );
|
||||
edd_set_upgrade_complete( 'stripe_customer_id_migration' );
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* Buy Now: AJAX
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a Download to the Cart on the `edds_add_to_cart` AJAX action.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_buy_now_ajax_add_to_cart() {
|
||||
$data = $_POST;
|
||||
|
||||
if ( ! isset( $data['download_id'] ) || ! isset( $data['nonce'] ) ) {
|
||||
return wp_send_json_error( array(
|
||||
'message' => __( 'Unable to add item to cart.', 'easy-digital-downloads' ),
|
||||
) );
|
||||
}
|
||||
|
||||
$download_id = absint( $data['download_id'] );
|
||||
$price_id = absint( $data['price_id'] );
|
||||
$quantity = absint( $data['quantity'] );
|
||||
|
||||
$nonce = sanitize_text_field( $data['nonce'] );
|
||||
$valid_nonce = wp_verify_nonce( $nonce, 'edd-add-to-cart-' . $download_id );
|
||||
|
||||
if ( false === $valid_nonce ) {
|
||||
return wp_send_json_error( array(
|
||||
'message' => __( 'Unable to add item to cart.', 'easy-digital-downloads' ),
|
||||
) );
|
||||
}
|
||||
|
||||
// Empty cart.
|
||||
edd_empty_cart();
|
||||
|
||||
// Add individual item.
|
||||
edd_add_to_cart( $download_id, array(
|
||||
'quantity' => $quantity,
|
||||
'price_id' => $price_id,
|
||||
) );
|
||||
|
||||
return wp_send_json_success( array(
|
||||
'checkout' => edds_buy_now_checkout(),
|
||||
) );
|
||||
}
|
||||
add_action( 'wp_ajax_edds_add_to_cart', 'edds_buy_now_ajax_add_to_cart' );
|
||||
add_action( 'wp_ajax_nopriv_edds_add_to_cart', 'edds_buy_now_ajax_add_to_cart' );
|
||||
|
||||
/**
|
||||
* Empties the cart on the `edds_buy_now_empty_cart` AJAX action.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_buy_now_ajax_empty_cart() {
|
||||
edd_empty_cart();
|
||||
|
||||
return wp_send_json_success();
|
||||
}
|
||||
add_action( 'wp_ajax_edds_empty_cart', 'edds_buy_now_ajax_empty_cart' );
|
||||
add_action( 'wp_ajax_nopriv_edds_empty_cart', 'edds_buy_now_ajax_empty_cart' );
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Buy Now: Checkout
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
// Hook in global card validation to this flow.
|
||||
add_action( 'edds_buy_now_checkout_error_checks', 'edds_process_post_data' );
|
||||
|
||||
/**
|
||||
* Adds Buy Now-specific overrides when processing a Buy Now purchase.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_buy_now_process_overrides() {
|
||||
if ( ! isset( $_POST ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_POST['edds-gateway'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'buy-now' !== $_POST['edds-gateway'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure Billing Address and Name Fields are not required.
|
||||
add_filter( 'edd_require_billing_address', '__return_false' );
|
||||
|
||||
// Require email address.
|
||||
add_filter( 'edd_purchase_form_required_fields', 'edds_buy_now_purchase_form_required_fields', 9999 );
|
||||
|
||||
// Remove 3rd party validations.
|
||||
remove_all_actions( 'edd_checkout_error_checks' );
|
||||
remove_all_actions( 'edd_checkout_user_error_checks' );
|
||||
|
||||
// Validate the form $_POST data.
|
||||
$valid_data = edd_purchase_form_validate_fields();
|
||||
|
||||
/**
|
||||
* Allows Buy Now-specific checkout validations.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $valid_data Validated checkout data.
|
||||
* @param array $_POST Global $_POST data.
|
||||
*/
|
||||
do_action( 'edds_buy_now_checkout_error_checks', $valid_data, $_POST );
|
||||
|
||||
// Validate the user
|
||||
$user = edd_get_purchase_form_user( $valid_data );
|
||||
|
||||
/**
|
||||
* Allows Buy Now-specific user validations.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $user Validated user data.
|
||||
* @param array $valid_data Validated checkout data.
|
||||
* @param array $_POST Global $_POST data.
|
||||
*/
|
||||
do_action( 'edds_buy_now_checkout_user_error_checks', $user, $valid_data, $_POST );
|
||||
}
|
||||
add_action( 'edd_pre_process_purchase', 'edds_buy_now_process_overrides' );
|
||||
|
||||
/**
|
||||
* Filters the purchase form's required field to only
|
||||
* require an email address.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function edds_buy_now_purchase_form_required_fields() {
|
||||
return array(
|
||||
'edd_email' => array(
|
||||
'error_id' => 'invalid_email',
|
||||
'error_message' => __( 'Please enter a valid email address', 'easy-digital-downloads' )
|
||||
),
|
||||
);
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Buy Now: Functions
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks to see if "Buy Now" is enabled.
|
||||
*
|
||||
* @since 2.8
|
||||
* @return boolean
|
||||
*/
|
||||
function edds_buy_now_is_enabled() {
|
||||
if ( false === edds_is_gateway_active() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the shop supports Buy Now.
|
||||
$shop_supports = edd_shop_supports_buy_now();
|
||||
|
||||
if ( false === $shop_supports ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if guest checkout is disabled and the user is not logged in.
|
||||
if ( edd_logged_in_only() && ! is_user_logged_in() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows "Buy Now" support if `stripe` and `stripe-prb` (Express Checkout) are
|
||||
* the only two active gateways and taxes are not enabled.
|
||||
*
|
||||
* @since 2.8
|
||||
* @param boolean $supports Whether the shop supports Buy Now, as determined by EDD.
|
||||
* @return boolean
|
||||
*/
|
||||
function edds_shop_supports_buy_now( $supports ) {
|
||||
|
||||
if ( edd_use_taxes() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$gateways = edd_get_enabled_payment_gateways();
|
||||
$stripe_gateways = array( 'stripe', 'stripe-prb' );
|
||||
$remaining_gateways = array_diff( array_keys( $gateways ), $stripe_gateways );
|
||||
|
||||
if ( empty( $remaining_gateways ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $supports;
|
||||
}
|
||||
add_filter( 'edd_shop_supports_buy_now', 'edds_shop_supports_buy_now' );
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Buy Now
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/buy-now/functions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/buy-now/ajax.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/buy-now/checkout.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/buy-now/template.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/buy-now/shortcode.php';
|
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* Buy Now: Shortcode
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the stripe-checkout parameter if the direct parameter is present in the [purchase_link] short code.
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @param array $out
|
||||
* @param array $pairs
|
||||
* @param array $atts
|
||||
* @return array
|
||||
*/
|
||||
function edd_stripe_purchase_link_shortcode_atts( $out, $pairs, $atts ) {
|
||||
|
||||
if ( ! edds_buy_now_is_enabled() ) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
$direct = false;
|
||||
|
||||
// [purchase_link direct=true]
|
||||
if ( isset( $atts['direct'] ) && true === edds_truthy_to_bool( $atts['direct'] ) ) {
|
||||
$direct = true;
|
||||
|
||||
// [purchase_link stripe-checkout]
|
||||
} else if ( isset( $atts['stripe-checkout'] ) || false !== array_search( 'stripe-checkout', $atts, true ) ) {
|
||||
$direct = true;
|
||||
}
|
||||
|
||||
$out['direct'] = $direct;
|
||||
|
||||
if ( true === $direct ) {
|
||||
$out['stripe-checkout'] = $direct;
|
||||
} else {
|
||||
unset( $out['stripe-checkout'] );
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
add_filter( 'shortcode_atts_purchase_link', 'edd_stripe_purchase_link_shortcode_atts', 10, 3 );
|
||||
|
||||
/**
|
||||
* Sets the stripe-checkout parameter if the direct parameter is present in edd_get_purchase_link()
|
||||
*
|
||||
* @since 2.0
|
||||
* @since 2.8.0 Adds `.edds-buy-now` to the class list.
|
||||
*
|
||||
* @param array $arg Purchase link shortcode attributes.
|
||||
* @return array
|
||||
*/
|
||||
function edd_stripe_purchase_link_atts( $args ) {
|
||||
global $edds_has_buy_now;
|
||||
|
||||
if ( ! edds_buy_now_is_enabled() ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
// Don't use modal if "Free Downloads" is active and available for this download.
|
||||
// https://easydigitaldownloads.com/downloads/free-downloads/
|
||||
if ( function_exists( 'edd_free_downloads_use_modal' ) ) {
|
||||
if ( edd_free_downloads_use_modal( $args['download_id'] ) && ! edd_has_variable_prices( $args['download_id'] ) ) {
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
|
||||
$direct = edds_truthy_to_bool( $args['direct'] );
|
||||
|
||||
$args['direct'] = $direct;
|
||||
|
||||
if ( true === $direct ) {
|
||||
$args['stripe-checkout'] = true;
|
||||
$args['class'] .= ' edds-buy-now';
|
||||
|
||||
if ( false === edd_item_in_cart( $args['download_id'] ) ) {
|
||||
$edds_has_buy_now = $direct;
|
||||
}
|
||||
} else {
|
||||
unset( $args['stripe-checkout'] );
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
add_filter( 'edd_purchase_link_args', 'edd_stripe_purchase_link_atts', 10 );
|
@ -0,0 +1,228 @@
|
||||
<?php
|
||||
/**
|
||||
* Buy Now: Template
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds "Buy Now" modal markup to the bottom of the page.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_buy_now_modal() {
|
||||
// Check if Stripe Buy Now is enabled.
|
||||
global $edds_has_buy_now;
|
||||
|
||||
if ( true !== $edds_has_buy_now ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! edds_buy_now_is_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue core scripts.
|
||||
add_filter( 'edd_is_checkout', '__return_true' );
|
||||
|
||||
if ( function_exists( 'edd_enqueue_scripts' ) ) {
|
||||
// https://github.com/easydigitaldownloads/easy-digital-downloads/issues/7847
|
||||
edd_enqueue_scripts();
|
||||
edd_localize_scripts();
|
||||
} else {
|
||||
edd_load_scripts();
|
||||
}
|
||||
|
||||
edd_agree_to_terms_js();
|
||||
|
||||
remove_filter( 'edd_is_checkout', '__return_true' );
|
||||
|
||||
// Enqueue scripts.
|
||||
edd_stripe_js( true );
|
||||
edd_stripe_css( true );
|
||||
|
||||
echo edds_modal( array(
|
||||
'id' => 'edds-buy-now',
|
||||
'title' => __( 'Buy Now', 'easy-digital-downloads' ),
|
||||
'class' => array(
|
||||
'edds-buy-now-modal',
|
||||
),
|
||||
'content' => '<span class="edd-loading-ajax edd-loading"></span>',
|
||||
) ); // WPCS: XSS okay.
|
||||
}
|
||||
add_action( 'wp_print_footer_scripts', 'edds_buy_now_modal', 0 );
|
||||
|
||||
/**
|
||||
* Outputs a custom "Buy Now"-specific Checkout form.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_buy_now_checkout() {
|
||||
$total = (int) edd_get_cart_total();
|
||||
|
||||
$form_mode = $total > 0
|
||||
? 'payment-mode=stripe'
|
||||
: 'payment-mode=manual';
|
||||
$form_action = edd_get_checkout_uri( $form_mode );
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
|
||||
$customer = EDD()->session->get( 'customer' );
|
||||
$customer = wp_parse_args(
|
||||
$customer,
|
||||
array(
|
||||
'email' => '',
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_user_logged_in() ) {
|
||||
$user_data = get_userdata( get_current_user_id() );
|
||||
|
||||
foreach( $customer as $key => $field ) {
|
||||
if ( 'email' == $key && empty( $field ) ) {
|
||||
$customer[ $key ] = $user_data->user_email;
|
||||
} elseif ( empty( $field ) ) {
|
||||
$customer[ $key ] = $user_data->$key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$customer = array_map( 'sanitize_text_field', $customer );
|
||||
|
||||
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields', 10 );
|
||||
remove_action( 'edd_purchase_form_before_submit', 'edd_checkout_final_total', 999 );
|
||||
|
||||
// Filter purchase button label.
|
||||
add_filter( 'edd_get_checkout_button_purchase_label', 'edds_buy_now_checkout_purchase_label' );
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div id="edd_checkout_form_wrap">
|
||||
<form
|
||||
id="edd_purchase_form"
|
||||
class="edd_form edds-buy-now-form"
|
||||
action="<?php echo esc_url( $form_action ); ?>"
|
||||
method="POST"
|
||||
>
|
||||
<p>
|
||||
<label class="edd-label" for="edd-email">
|
||||
<?php esc_html_e( 'Email Address', 'easy-digital-downloads' ); ?>
|
||||
<?php if ( edd_field_is_required( 'edd_email' ) ) : ?>
|
||||
<span class="edd-required-indicator">*</span>
|
||||
<?php endif ?>
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="edd-email"
|
||||
class="edd-input required"
|
||||
type="email"
|
||||
name="edd_email"
|
||||
value="<?php echo esc_attr( $customer['email'] ); ?>"
|
||||
<?php if ( edd_field_is_required( 'edd_email' ) ) : ?>
|
||||
required
|
||||
<?php endif; ?>
|
||||
/>
|
||||
</p>
|
||||
|
||||
<?php if ( $total > 0 ) : ?>
|
||||
|
||||
<?php if ( ! empty( $existing_cards ) ) : ?>
|
||||
<?php edd_stripe_existing_card_field_radio( get_current_user_id() ); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div
|
||||
class="edd-stripe-new-card"
|
||||
<?php if ( ! empty( $existing_cards ) ) : ?>
|
||||
style="display: none;"
|
||||
<?php endif; ?>
|
||||
>
|
||||
<?php do_action( 'edd_stripe_new_card_form' ); ?>
|
||||
<?php do_action( 'edd_after_cc_expiration' ); ?>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
edd_terms_agreement();
|
||||
edd_privacy_agreement();
|
||||
edd_checkout_hidden_fields();
|
||||
?>
|
||||
|
||||
<div id="edd_purchase_submit">
|
||||
<?php echo edds_get_tokenizer_input(); // WPCS: XSS okay. ?>
|
||||
<?php echo edd_checkout_button_purchase(); // WPCS: XSS okay. ?>
|
||||
</div>
|
||||
|
||||
<div class="edd_cart_total" style="display: none;">
|
||||
<div
|
||||
class="edd_cart_amount"
|
||||
data-total="<?php echo edd_get_cart_total(); ?>"
|
||||
data-total-currency="<?php echo edd_currency_filter( edd_format_amount( edd_get_cart_total() ) ); ?>"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="edds-gateway" value="buy-now" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the label of the of the "Purchase" button in the "Buy Now" modal.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param string $label Purchase label.
|
||||
* @return string
|
||||
*/
|
||||
function edds_buy_now_checkout_purchase_label( $label ) {
|
||||
$total = edd_get_cart_total();
|
||||
|
||||
if ( 0 === (int) $total ) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%s - %s',
|
||||
edd_currency_filter(
|
||||
edd_format_amount( $total )
|
||||
),
|
||||
$label
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional script variables needed for the Buy Now flow.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $vars Script variables.
|
||||
* @return array
|
||||
*/
|
||||
function edds_buy_now_vars( $vars ) {
|
||||
if ( ! isset( $vars['i18n'] ) ) {
|
||||
$vars['i18n'] = array();
|
||||
}
|
||||
|
||||
// Non-zero amount.
|
||||
$label = edd_get_option( 'checkout_label', '' );
|
||||
$complete_purchase = ! empty( $label )
|
||||
? $label
|
||||
: esc_html__( 'Purchase', 'easy-digital-downloads' );
|
||||
|
||||
/* This filter is documented in easy-digital-downloads/includes/checkout/template.php */
|
||||
$complete_purchase = apply_filters(
|
||||
'edd_get_checkout_button_purchase_label',
|
||||
$complete_purchase,
|
||||
$label
|
||||
);
|
||||
|
||||
$vars['i18n']['completePurchase'] = $complete_purchase;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
add_filter( 'edd_stripe_js_vars', 'edds_buy_now_vars' );
|
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request Button: Settings
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds settings to the Stripe subtab.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $settings Gateway settings.
|
||||
* @return array Filtered gateway settings.
|
||||
*/
|
||||
function edds_prb_add_settings( $settings ) {
|
||||
// Prevent adding the extra settings if the requirements are not met.
|
||||
// The `edd_settings_gateways` filter runs regardless of the short circuit
|
||||
// inside of `edds_add_settings()`
|
||||
if (
|
||||
false === edds_has_met_requirements( 'php' ) &&
|
||||
false === edds_is_pro()
|
||||
) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
if ( true === edd_use_taxes() ) {
|
||||
$prb_settings = array(
|
||||
array(
|
||||
'id' => 'stripe_prb_taxes',
|
||||
'name' => __( 'Apple Pay/Google Pay', 'easy-digital-downloads' ),
|
||||
'type' => 'edds_stripe_prb_taxes',
|
||||
),
|
||||
);
|
||||
} else {
|
||||
$prb_settings = array(
|
||||
array(
|
||||
'id' => 'stripe_prb',
|
||||
'name' => __( 'Apple Pay/Google Pay', 'easy-digital-downloads' ),
|
||||
'desc' => wp_kses(
|
||||
(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Opening anchor tag, do not translate. %3$s Closing anchor tag, do not translate. */
|
||||
__( '"Express Checkout" via Apple Pay, Google Pay, or Microsoft Pay digital wallets. By using Apple Pay, you agree to %1$sStripe%3$s and %2$sApple\'s%3$s terms of service.', 'easy-digital-downloads' ),
|
||||
'<a href="https://stripe.com/apple-pay/legal" target="_blank" rel="noopener noreferrer">',
|
||||
'<a href="https://developer.apple.com/apple-pay/acceptable-use-guidelines-for-websites/" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
) . (
|
||||
edd_is_test_mode()
|
||||
? '<br /><strong>' . __( 'Apple Pay is not available in Test Mode.', 'easy-digital-downloads' ) . '</strong> ' . sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Opening anchor tag, do not translate. */
|
||||
__( 'See our %1$sdocumentation%2$s for more information.', 'easy-digital-downloads' ),
|
||||
'<a href="' . esc_url( edds_documentation_route( 'stripe-express-checkout' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
)
|
||||
: ''
|
||||
)
|
||||
),
|
||||
array(
|
||||
'br' => true,
|
||||
'strong' => true,
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'target' => true,
|
||||
'rel' => true,
|
||||
),
|
||||
)
|
||||
),
|
||||
'type' => 'multicheck',
|
||||
'options' => array(
|
||||
/** translators: %s Download noun */
|
||||
'single' => sprintf(
|
||||
__( 'Single %s', 'easy-digital-downloads' ),
|
||||
edd_get_label_singular()
|
||||
),
|
||||
/** translators: %s Download noun */
|
||||
'archive' => sprintf(
|
||||
__( '%s Archive (includes <code>[downloads]</code> shortcode)', 'easy-digital-downloads' ),
|
||||
edd_get_label_singular()
|
||||
),
|
||||
'checkout' => __( 'Checkout', 'easy-digital-downloads' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$position = array_search(
|
||||
'stripe_statement_descriptor',
|
||||
array_values( wp_list_pluck( $settings['edd-stripe'], 'id' ) ),
|
||||
true
|
||||
);
|
||||
|
||||
$settings['edd-stripe'] = array_merge(
|
||||
array_slice( $settings['edd-stripe'], 0, $position + 1 ),
|
||||
$prb_settings,
|
||||
array_slice( $settings['edd-stripe'], $position + 1 )
|
||||
);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
add_filter( 'edd_settings_gateways', 'edds_prb_add_settings', 20 );
|
||||
|
||||
/**
|
||||
* Removes multicheck options and outputs a message about "Express Checkout" incompatibility with taxes.
|
||||
*
|
||||
* @since 2.8.7
|
||||
*/
|
||||
function edd_edds_stripe_prb_taxes_callback() {
|
||||
echo esc_html__(
|
||||
'This feature is not available when taxes are enabled.',
|
||||
'easy-digital-downloads'
|
||||
) . ' ';
|
||||
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
__(
|
||||
'See the %1$sExpress Checkout documentation%2$s for more information.',
|
||||
'easy-digital-downloads'
|
||||
),
|
||||
'<a href="' . esc_url( edds_documentation_route( 'stripe-express-checkout' ) ) . '#edds-prb-faqs" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'target' => true,
|
||||
'rel' => true,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force "Payment Request Buttons" to be disabled if taxes are enabled.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param mixed $value Setting value.
|
||||
* @param string $key Setting key.
|
||||
* @return string Setting value.
|
||||
*/
|
||||
function edds_prb_sanitize_setting( $value, $key ) {
|
||||
if ( 'stripe_prb' === $key && edd_use_taxes() ) {
|
||||
$value = array();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
add_filter( 'edd_settings_sanitize_multicheck', 'edds_prb_sanitize_setting', 10, 2 );
|
@ -0,0 +1,253 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request Button: AJAX
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Starts the Checkout process for a Payment Request.
|
||||
*
|
||||
* This needs to be used instead of `_edds_process_purchase_form()` so
|
||||
* Checkout form data can be shimmed or faked to prevent getting hung
|
||||
* up on things like privacy policy, terms of service, etc.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_ajax_process_checkout() {
|
||||
// Clear any errors that might be used as a reason to attempt a redirect in the following action.
|
||||
edd_clear_errors();
|
||||
|
||||
$download_id = isset( $_POST['downloadId'] )
|
||||
? intval( $_POST['downloadId'] )
|
||||
: 0;
|
||||
|
||||
$email = isset( $_POST['email'] )
|
||||
? sanitize_text_field( $_POST['email'] )
|
||||
: '';
|
||||
|
||||
$name = isset( $_POST['name'] )
|
||||
? sanitize_text_field( $_POST['name'] )
|
||||
: '';
|
||||
|
||||
$payment_method = isset( $_POST['paymentMethod'] )
|
||||
? $_POST['paymentMethod']
|
||||
: '';
|
||||
|
||||
$context = isset( $_POST['context'] )
|
||||
? $_POST['context']
|
||||
: 'checkout';
|
||||
|
||||
// Add a Download to the cart if we are not processing the full cart.
|
||||
if ( 'download' === $context ) {
|
||||
$price_id = isset( $_POST['priceId'] )
|
||||
? intval( $_POST['priceId'] )
|
||||
: false;
|
||||
|
||||
$quantity = isset( $_POST['quantity'] )
|
||||
? intval( $_POST['quantity'] )
|
||||
: 1;
|
||||
|
||||
// Empty cart.
|
||||
edd_empty_cart();
|
||||
|
||||
// Add individual item.
|
||||
edd_add_to_cart( $download_id, array(
|
||||
'quantity' => $quantity,
|
||||
'price_id' => $price_id,
|
||||
) );
|
||||
|
||||
// Refilter guest checkout when the item is added to the cart dynamically.
|
||||
// This is a duplicate of EDD_Recurring_Gateway::require_login().
|
||||
if ( defined( 'EDD_RECURRING_VERSION' ) ) {
|
||||
$cart_items = edd_get_cart_contents();
|
||||
$has_recurring = false;
|
||||
$auto_register = class_exists( 'EDD_Auto_Register' );
|
||||
|
||||
if ( ! empty( $cart_items ) ) {
|
||||
foreach ( $cart_items as $item ) {
|
||||
if ( ! isset( $item['options']['recurring'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$has_recurring = true;
|
||||
}
|
||||
|
||||
if ( $has_recurring && ! $auto_register ) {
|
||||
add_filter( 'edd_no_guest_checkout', '__return_true' );
|
||||
add_filter( 'edd_logged_in_only', '__return_true' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$data = array(
|
||||
// Mark "sub-gateway" for Stripe. This represents the Payment Method
|
||||
// currently being used. e.g `ideal`, `wepay`, `payment-request`, etc.
|
||||
//
|
||||
// This is used to filter field requirements via `edd_pre_process_purchase` hook.
|
||||
'edds-gateway' => 'payment-request',
|
||||
'edds-prb-context' => $context,
|
||||
);
|
||||
|
||||
// Checkout-specific data.
|
||||
if ( 'checkout' === $context ) {
|
||||
$form_data = isset( $_POST['form_data'] )
|
||||
? $_POST['form_data']
|
||||
: array();
|
||||
|
||||
// Use the Payment Method's billing details.
|
||||
$card_name = (
|
||||
! empty( $payment_method['billing_details'] ) &&
|
||||
! empty( $payment_method['billing_details']['name'] )
|
||||
)
|
||||
? $payment_method['billing_details']['name']
|
||||
: $name;
|
||||
|
||||
$billing_details = ! empty( $payment_method['billing_details'] )
|
||||
? array(
|
||||
'card_name' => $card_name,
|
||||
'card_address' => $payment_method['billing_details']['address']['line1'],
|
||||
'card_address_2' => $payment_method['billing_details']['address']['line2'],
|
||||
'card_city' => $payment_method['billing_details']['address']['city'],
|
||||
'card_zip' => $payment_method['billing_details']['address']['postal_code'],
|
||||
'billing_country' => $payment_method['billing_details']['address']['country'],
|
||||
'card_state' => $payment_method['billing_details']['address']['state'],
|
||||
)
|
||||
: array(
|
||||
'card_name' => $card_name,
|
||||
'card_address' => '',
|
||||
'card_address_2' => '',
|
||||
'card_city' => '',
|
||||
'card_zip' => '',
|
||||
'billing_country' => '',
|
||||
'card_state' => '',
|
||||
);
|
||||
|
||||
// Add the Payment Request's name as the card name.
|
||||
$_POST['form_data'] = add_query_arg(
|
||||
$billing_details,
|
||||
$form_data
|
||||
);
|
||||
|
||||
// Single-download data.
|
||||
} else {
|
||||
// Fake checkout form data.
|
||||
$_POST['form_data'] = http_build_query(
|
||||
array_merge(
|
||||
$data,
|
||||
array(
|
||||
// Use Email from Payment Request.
|
||||
'edd_email' => $email,
|
||||
|
||||
'edd-user-id' => get_current_user_id(),
|
||||
'edd_action' => 'purchase',
|
||||
'edd-gateway' => 'stripe',
|
||||
'edd_agree_to_terms' => '1',
|
||||
'edd_agree_to_privacy_policy' => '1',
|
||||
'edd-process-checkout-nonce' => wp_create_nonce( 'edd-process-checkout' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$_POST['payment_method_id'] = isset( $payment_method['id'] )
|
||||
? sanitize_text_field( $payment_method['id'] )
|
||||
: '';
|
||||
|
||||
$_POST['payment_method_exists'] = false;
|
||||
|
||||
// Adjust PaymentIntent creation for PRB flow.
|
||||
add_filter( 'edds_create_payment_intent_args', 'edds_prb_create_payment_intent_args', 20 );
|
||||
add_filter( 'edds_create_setup_intent_args', 'edds_prb_create_setup_intent_args', 20 );
|
||||
|
||||
// This will send a JSON response.
|
||||
_edds_process_purchase_form();
|
||||
} catch ( \Exception $e ) {
|
||||
wp_send_json_error( array(
|
||||
'message' => esc_html( $e->getMessage() ),
|
||||
) );
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edds_prb_ajax_process_checkout', 'edds_prb_ajax_process_checkout' );
|
||||
add_action( 'wp_ajax_nopriv_edds_prb_ajax_process_checkout', 'edds_prb_ajax_process_checkout' );
|
||||
|
||||
/**
|
||||
* Filters the arguments used when creating a PaymentIntent while
|
||||
* using a Payment Request Button.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $args {
|
||||
* PaymentIntent arguments.
|
||||
*
|
||||
* @link https://stripe.com/docs/api/payment_intents/create
|
||||
* }
|
||||
* @return array
|
||||
*/
|
||||
function edds_prb_create_payment_intent_args( $args ) {
|
||||
$args['confirmation_method'] = 'automatic';
|
||||
$args['confirm'] = false;
|
||||
$args['capture_method'] = 'automatic';
|
||||
$args['metadata']['edds_prb'] = '1';
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the arguments used when creating a SetupIntent while
|
||||
* using a Payment Request Button.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $args {
|
||||
* SetupIntent arguments.
|
||||
*
|
||||
* @link https://stripe.com/docs/api/setup_intents/create
|
||||
* }
|
||||
* @return array
|
||||
*/
|
||||
function edds_prb_create_setup_intent_args( $args ) {
|
||||
$args['confirm'] = false;
|
||||
$args['metadata']['edds_prb'] = '1';
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers Payment Request options based on the current context.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_ajax_get_options() {
|
||||
$download_id = isset( $_POST['downloadId'] )
|
||||
? intval( $_POST['downloadId'] )
|
||||
: 0;
|
||||
|
||||
// Single Download.
|
||||
if ( ! empty( $download_id ) ) {
|
||||
$price_id = isset( $_POST['priceId'] ) && 'false' !== $_POST['priceId']
|
||||
? intval( $_POST['priceId'] )
|
||||
: false;
|
||||
|
||||
$quantity = isset( $_POST['quantity'] )
|
||||
? intval( $_POST['quantity'] )
|
||||
: 1;
|
||||
|
||||
$data = edds_prb_get_download_data( $download_id, $price_id, $quantity );
|
||||
|
||||
// Handle cart eventually?
|
||||
} else {
|
||||
$data = edds_prb_get_cart_data();
|
||||
}
|
||||
|
||||
// Country is not valid at this point.
|
||||
// https://stripe.com/docs/js/payment_request/update
|
||||
unset( $data['country'] );
|
||||
|
||||
wp_send_json_success( $data );
|
||||
}
|
||||
add_action( 'wp_ajax_edds_prb_ajax_get_options', 'edds_prb_ajax_get_options' );
|
||||
add_action( 'wp_ajax_nopriv_edds_prb_ajax_get_options', 'edds_prb_ajax_get_options' );
|
@ -0,0 +1,280 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request: Apple Pay
|
||||
*
|
||||
* @link https://stripe.com/docs/stripe-js/elements/payment-request-button#verifying-your-domain-with-apple-pay
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers admin notices.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return true|WP_Error True if all notices are registered, otherwise WP_Error.
|
||||
*/
|
||||
function edds_prb_apple_pay_admin_notices_register() {
|
||||
$registry = edds_get_registry( 'admin-notices' );
|
||||
|
||||
if ( ! $registry ) {
|
||||
return new WP_Error( 'edds-invalid-registry', esc_html__( 'Unable to locate registry', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
try {
|
||||
// General error message.
|
||||
$message = (
|
||||
'<strong>' . esc_html__( 'Apple Pay domain verification error.', 'easy-digital-downloads' ) . '</strong><br />' .
|
||||
edd_get_option( 'stripe_prb_apple_pay_domain_error', '' )
|
||||
);
|
||||
|
||||
$registry->add(
|
||||
'apple-pay-' . $_SERVER['HTTP_HOST'],
|
||||
array(
|
||||
'message' => wp_kses(
|
||||
wpautop( $message ),
|
||||
array(
|
||||
'code' => true,
|
||||
'br' => true,
|
||||
'strong' => true,
|
||||
'p' => true,
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
'rel' => true,
|
||||
'target' => true,
|
||||
),
|
||||
)
|
||||
),
|
||||
'type' => 'error',
|
||||
'dismissible' => true,
|
||||
)
|
||||
);
|
||||
} catch( Exception $e ) {
|
||||
return new WP_Error( 'edds-invalid-notices-registration', esc_html( $e->getMessage() ) );
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
add_action( 'admin_init', 'edds_prb_apple_pay_admin_notices_register', 30 );
|
||||
|
||||
/**
|
||||
* Conditionally prints registered notices.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_apple_pay_admin_notices_print() {
|
||||
// Current user needs capability to dismiss notices.
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$registry = edds_get_registry( 'admin-notices' );
|
||||
|
||||
if ( ! $registry ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = new EDD_Stripe_Admin_Notices( $registry );
|
||||
|
||||
wp_enqueue_script( 'edds-admin-notices' );
|
||||
|
||||
try {
|
||||
$error = edd_get_option( 'stripe_prb_apple_pay_domain_error', '' );
|
||||
$test_mode = edd_is_test_mode();
|
||||
|
||||
if ( ! empty( $error ) && false === $test_mode ) {
|
||||
$notices->output( 'apple-pay-' . $_SERVER['HTTP_HOST'] );
|
||||
}
|
||||
} catch( Exception $e ) {}
|
||||
}
|
||||
add_action( 'admin_notices', 'edds_prb_apple_pay_admin_notices_print' );
|
||||
|
||||
/**
|
||||
* Returns information associated with the name/location of the domain verification file.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return array Domain verification file information.
|
||||
*/
|
||||
function edds_prb_apple_pay_get_fileinfo() {
|
||||
$path = untrailingslashit( $_SERVER['DOCUMENT_ROOT'] );
|
||||
$dir = '.well-known';
|
||||
$file = 'apple-developer-merchantid-domain-association';
|
||||
|
||||
return array(
|
||||
'path' => $path,
|
||||
'dir' => $dir,
|
||||
'file' => $file,
|
||||
'fullpath' => $path . '/' . $dir . '/' . $file,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current website is setup to use Apple Pay.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return bool True if the domain has been verified and the association file exists.
|
||||
*/
|
||||
function edds_prb_apple_pay_is_valid() {
|
||||
return (
|
||||
edds_prb_apple_pay_has_domain_verification_file() &&
|
||||
edds_prb_apple_pay_has_domain_verification()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the domain verification file already exists.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return bool True if the domain verification file exists.
|
||||
*/
|
||||
function edds_prb_apple_pay_has_domain_verification_file() {
|
||||
$fileinfo = edds_prb_apple_pay_get_fileinfo();
|
||||
|
||||
if ( ! @file_exists( $fileinfo['fullpath'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the currently verified domain matches the current site.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return bool True if the saved verified domain matches the current site.
|
||||
*/
|
||||
function edds_prb_apple_pay_has_domain_verification() {
|
||||
return edd_get_option( 'stripe_prb_apple_pay_domain' ) === $_SERVER['HTTP_HOST'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a directory in the server root and copy the domain verification file.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @throws \Exception If the directory or file cannot be created.
|
||||
*/
|
||||
function edds_prb_apple_pay_create_directory_and_move_file() {
|
||||
$file = edds_prb_apple_pay_has_domain_verification_file();
|
||||
|
||||
if ( true === $file ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fileinfo = edds_prb_apple_pay_get_fileinfo();
|
||||
|
||||
// Create directory if it does not exist.
|
||||
if ( ! file_exists( trailingslashit( $fileinfo['path'] ) . $fileinfo['dir'] ) ) {
|
||||
if ( ! @mkdir( trailingslashit( $fileinfo['path'] ) . $fileinfo['dir'], 0755 ) ) { // @codingStandardsIgnoreLine
|
||||
throw new \Exception( __( 'Unable to create domain association folder in domain root.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Move file if needed.
|
||||
if ( ! edds_prb_apple_pay_has_domain_verification_file() ) {
|
||||
if ( ! @copy( trailingslashit( EDDS_PLUGIN_DIR ) . $fileinfo['file'], $fileinfo['fullpath'] ) ) { // @codingStandardsIgnoreLine
|
||||
throw new \Exception( __( 'Unable to copy domain association file to domain .well-known directory.', 'easy-digital-downloads' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks Apple Pay domain verification if there is an existing error.
|
||||
* If the domain was added to the Stripe Dashboard clear the error.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_apple_pay_check_domain() {
|
||||
$error = edd_get_option( 'stripe_prb_apple_pay_domain_error', '' );
|
||||
|
||||
if ( empty( $error ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$domains = edds_api_request( 'ApplePayDomain', 'all' );
|
||||
|
||||
foreach ( $domains->autoPagingIterator() as $domain ) {
|
||||
if ( $domain->domain_name === $_SERVER['HTTP_HOST'] ) {
|
||||
edd_delete_option( 'stripe_prb_apple_pay_domain_error' );
|
||||
edd_update_option( 'stripe_prb_apple_pay_domain', $_SERVER['HTTP_HOST'] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch ( \Exception $e ) {}
|
||||
}
|
||||
add_action( 'admin_init', 'edds_prb_apple_pay_check_domain', 10 );
|
||||
|
||||
/**
|
||||
* Verifies the current domain.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_apple_pay_verify_domain() {
|
||||
// Payment Request Button is not enabled, do nothing.
|
||||
if ( false === edds_prb_is_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid getting caught in AJAX requests.
|
||||
if ( defined( 'DOING_AJAX' ) && true === DOING_AJAX ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Must be verified in Live Mode.
|
||||
if ( true === edd_is_test_mode() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Current site is a development environment, Apple Pay won't be able to be used, do nothing.
|
||||
if ( false !== edd_is_dev_environment() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Current domain matches and the file exists, do nothing.
|
||||
if ( true === edds_prb_apple_pay_is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create directory and move file if needed.
|
||||
edds_prb_apple_pay_create_directory_and_move_file();
|
||||
|
||||
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id', '' );
|
||||
|
||||
// Automatically verify when using "real" API keys.
|
||||
if ( empty( $stripe_connect_account_id ) ) {
|
||||
$verification = edds_api_request(
|
||||
'ApplePayDomain',
|
||||
'create',
|
||||
array(
|
||||
'domain_name' => $_SERVER['HTTP_HOST'],
|
||||
)
|
||||
);
|
||||
|
||||
edd_update_option( 'stripe_prb_apple_pay_domain', $_SERVER['HTTP_HOST'] );
|
||||
|
||||
// Set an error that the domain needs to be manually added.
|
||||
// Using Stripe Connect API keys does not allow this to be done automatically.
|
||||
} else {
|
||||
throw new \Exception(
|
||||
sprintf(
|
||||
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
|
||||
( __( 'Please %1$smanually add your domain%2$s %3$s to use Apple Pay.', 'easy-digital-downloads' ) . '<br />' ),
|
||||
'<a href="https://dashboard.stripe.com/settings/payments/apple_pay" target="_blank" rel="noopener noreferrer">',
|
||||
'</a>',
|
||||
'<code>' . $_SERVER['HTTP_HOST'] . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
// Set error if something went wrong.
|
||||
edd_update_option( 'stripe_prb_apple_pay_domain_error', $e->getMessage() );
|
||||
}
|
||||
}
|
||||
add_action( 'admin_init', 'edds_prb_apple_pay_verify_domain', 20 );
|
@ -0,0 +1,377 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request Button: Checkout
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers "Express" (via Apple Pay/Google Pay) gateway.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $gateways Registered payment gateways.
|
||||
* @return array
|
||||
*/
|
||||
function edds_prb_shim_gateways( $gateways ) {
|
||||
// Do nothing in admin.
|
||||
if ( is_admin() ) {
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
// Avoid endless loops when checking if the Stripe gateway is active.
|
||||
remove_filter( 'edd_payment_gateways', 'edds_prb_shim_gateways' );
|
||||
remove_filter( 'edd_enabled_payment_gateways', 'edds_prb_shim_gateways' );
|
||||
|
||||
$enabled = true;
|
||||
|
||||
// Do nothing if Payment Requests are not enabled.
|
||||
if ( false === edds_prb_is_enabled( 'checkout' ) ) {
|
||||
$enabled = false;
|
||||
}
|
||||
|
||||
// Track default gateway so we can resort the list.
|
||||
$default_gateway_id = edd_get_default_gateway();
|
||||
|
||||
if ( 'stripe-prb' === $default_gateway_id ) {
|
||||
$default_gateway_id = 'stripe';
|
||||
}
|
||||
|
||||
// Avoid endless loops when checking if the Stripe gateway is active.
|
||||
add_filter( 'edd_payment_gateways', 'edds_prb_shim_gateways' );
|
||||
add_filter( 'edd_enabled_payment_gateways', 'edds_prb_shim_gateways' );
|
||||
|
||||
if ( false === $enabled ) {
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
// Ensure default gateway is considered registered at this point.
|
||||
if ( isset( $gateways[ $default_gateway_id ] ) ) {
|
||||
$default_gateway = array(
|
||||
$default_gateway_id => $gateways[ $default_gateway_id ],
|
||||
);
|
||||
|
||||
// Fall back to first gateway in the list.
|
||||
} else {
|
||||
if ( function_exists( 'array_key_first' ) ) {
|
||||
$first_gateway_id = array_key_first( $gateways );
|
||||
} else {
|
||||
$gateway_keys = array_keys( $gateways );
|
||||
$first_gateway_id = reset( $gateway_keys );
|
||||
}
|
||||
$default_gateway = array(
|
||||
$first_gateway_id => $gateways[ $first_gateway_id ],
|
||||
);
|
||||
}
|
||||
|
||||
unset( $gateways[ $default_gateway_id ] );
|
||||
|
||||
return array_merge(
|
||||
array(
|
||||
'stripe-prb' => array(
|
||||
'admin_label' => __( 'Express Checkout (Apple Pay/Google Pay)', 'easy-digital-downloads' ),
|
||||
'checkout_label' => __( 'Express Checkout', 'easy-digital-downloads' ),
|
||||
'supports' => array(),
|
||||
),
|
||||
),
|
||||
$default_gateway,
|
||||
$gateways
|
||||
);
|
||||
}
|
||||
add_filter( 'edd_payment_gateways', 'edds_prb_shim_gateways' );
|
||||
add_filter( 'edd_enabled_payment_gateways', 'edds_prb_shim_gateways' );
|
||||
|
||||
/**
|
||||
* Enables the shimmed `stripe-prb` gateway.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $gateways Enabled payment gateways.
|
||||
* @return array
|
||||
*/
|
||||
function edds_prb_enable_shim_gateway( $gateways ) {
|
||||
// Do nothing in admin.
|
||||
if ( is_admin() ) {
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
// Avoid endless loops when checking if the Stripe gateway is active.
|
||||
remove_filter( 'edd_get_option_gateways', 'edds_prb_enable_shim_gateway' );
|
||||
|
||||
$enabled = true;
|
||||
|
||||
// Do nothing if Payment Requests are not enabled.
|
||||
if ( false === edds_prb_is_enabled( 'checkout' ) ) {
|
||||
$enabled = false;
|
||||
}
|
||||
|
||||
// Avoid endless loops when checking if the Stripe gateway is active.
|
||||
add_filter( 'edd_get_option_gateways', 'edds_prb_enable_shim_gateway' );
|
||||
|
||||
if ( false === $enabled ) {
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
$gateways['stripe-prb'] = 1;
|
||||
|
||||
return $gateways;
|
||||
}
|
||||
add_filter( 'edd_get_option_gateways', 'edds_prb_enable_shim_gateway' );
|
||||
|
||||
/**
|
||||
* Ensures the base `stripe` gateway is used as an ID _only_ when generating
|
||||
* the hidden `input[name="edd-gateway"]` field.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_shim_active_gateways() {
|
||||
add_filter( 'edd_chosen_gateway', 'edds_prb_set_base_gateway' );
|
||||
add_filter( 'edd_is_gateway_active', 'edds_prb_is_gateway_active', 10, 2 );
|
||||
}
|
||||
add_action( 'edd_purchase_form_before_submit', 'edds_prb_shim_active_gateways' );
|
||||
|
||||
/**
|
||||
* Removes conversion of `stripe-prb` to `stripe` after the `input[name="edd-gateway"]`
|
||||
* hidden input is generated.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_unshim_active_gateways() {
|
||||
remove_filter( 'edd_chosen_gateway', 'edds_prb_set_base_gateway' );
|
||||
remove_filter( 'edd_is_gateway_active', 'edds_prb_is_gateway_active', 10, 2 );
|
||||
}
|
||||
add_action( 'edd_purchase_form_after_submit', 'edds_prb_unshim_active_gateways' );
|
||||
|
||||
/**
|
||||
* Ensures the "Express Checkout" gateway is considered active if the setting
|
||||
* is enabled.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param bool $active Determines if the gateway is considered active.
|
||||
* @param string $gateway The gateway ID to check.
|
||||
* @return bool
|
||||
*/
|
||||
function edds_prb_is_gateway_active( $active, $gateway ) {
|
||||
remove_filter( 'edd_is_gateway_active', 'edds_prb_is_gateway_active', 10, 2 );
|
||||
|
||||
if (
|
||||
'stripe-prb' === $gateway &&
|
||||
true === edds_prb_is_enabled( 'checkout' )
|
||||
) {
|
||||
$active = true;
|
||||
}
|
||||
|
||||
add_filter( 'edd_is_gateway_active', 'edds_prb_is_gateway_active', 10, 2 );
|
||||
|
||||
return $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the found active `stripe-prb` Express Checkout gateway back
|
||||
* to the base `stripe` gateway ID.
|
||||
*
|
||||
* @param string $gateway Chosen payment gateway.
|
||||
* @return string
|
||||
*/
|
||||
function edds_prb_set_base_gateway( $gateway ) {
|
||||
if ( 'stripe-prb' === $gateway ) {
|
||||
$gateway = 'stripe';
|
||||
}
|
||||
|
||||
return $gateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the default gateway.
|
||||
*
|
||||
* Sets the Payment Request Button (Express Checkout) as default
|
||||
* when enabled for the context.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param string $default Default gateway.
|
||||
* @return string
|
||||
*/
|
||||
function edds_prb_default_gateway( $default ) {
|
||||
// Do nothing in admin.
|
||||
if ( is_admin() ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
// Avoid endless loops when checking if the Stripe gateway is active.
|
||||
remove_filter( 'edd_default_gateway', 'edds_prb_default_gateway' );
|
||||
|
||||
$enabled = true;
|
||||
|
||||
// Do nothing if Payment Requests are not enabled.
|
||||
if ( false === edds_prb_is_enabled( 'checkout' ) ) {
|
||||
$enabled = false;
|
||||
}
|
||||
|
||||
// Avoid endless loops when checking if the Stripe gateway is active.
|
||||
add_filter( 'edd_default_gateway', 'edds_prb_default_gateway' );
|
||||
|
||||
if ( false === $enabled ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return 'stripe' === $default
|
||||
? 'stripe-prb'
|
||||
: $default;
|
||||
}
|
||||
add_filter( 'edd_default_gateway', 'edds_prb_default_gateway' );
|
||||
|
||||
/**
|
||||
* Adds Payment Request-specific overrides when processing a single Download.
|
||||
*
|
||||
* Disables all required fields.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_process_overrides() {
|
||||
if ( ! isset( $_POST ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_POST['edds-gateway'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'payment-request' !== $_POST['edds-gateway'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'download' !== $_POST['edds-prb-context'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure Billing Address and Name Fields are not required.
|
||||
add_filter( 'edd_require_billing_address', '__return_false' );
|
||||
|
||||
// Require email address.
|
||||
add_filter( 'edd_purchase_form_required_fields', 'edds_prb_purchase_form_required_fields', 9999 );
|
||||
|
||||
// Remove 3rd party validations.
|
||||
remove_all_actions( 'edd_checkout_error_checks' );
|
||||
remove_all_actions( 'edd_checkout_user_error_checks' );
|
||||
}
|
||||
add_action( 'edd_pre_process_purchase', 'edds_prb_process_overrides' );
|
||||
|
||||
/**
|
||||
* Filters the purchase form's required field to only
|
||||
* require an email address.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function edds_prb_purchase_form_required_fields() {
|
||||
return array(
|
||||
'edd_email' => array(
|
||||
'error_id' => 'invalid_email',
|
||||
'error_message' => __( 'Please enter a valid email address', 'easy-digital-downloads' )
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a note and metadata to Payments made with a Payment Request Button.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param \EDD_Payment $payment EDD Payment.
|
||||
* @param \Stripe\PaymentIntent|\Stripe\SetupIntent $intent Created Stripe Intent.
|
||||
*/
|
||||
function edds_prb_payment_created( $payment, $intent ) {
|
||||
if ( false === isset( $intent['metadata']['edds_prb'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment->update_meta( '_edds_stripe_prb', 1 );
|
||||
$payment->add_note( 'Purchase completed with Express Checkout (Apple Pay/Google Pay)' );
|
||||
}
|
||||
add_action( 'edds_payment_created', 'edds_prb_payment_created', 10, 2 );
|
||||
|
||||
/**
|
||||
* Creates an empty Credit Card form to ensure core actions are still called.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_cc_form() {
|
||||
/* This action is documented in easy-digital-downloads/includes/checkout/template.php */
|
||||
do_action( 'edd_before_cc_fields' );
|
||||
|
||||
/* This action is documented in easy-digital-downloads/includes/checkout/template.php */
|
||||
do_action( 'edd_after_cc_fields' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Payment Request gateway.
|
||||
*
|
||||
* This fires before core's callbacks to avoid firing additional
|
||||
* actions (and therefore creating extra output) when using the Payment Request.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_load_gateway() {
|
||||
if ( ! isset( $_POST['nonce'] ) ) {
|
||||
edd_debug_log(
|
||||
__( 'Missing nonce when loading the gateway fields. Please read the following for more information: https://easydigitaldownloads.com/development/2018/07/05/important-update-to-ajax-requests-in-easy-digital-downloads-2-9-4', 'easy-digital-downloads' ),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $_POST['edd_payment_mode'] ) && isset( $_POST['nonce'] ) ) {
|
||||
$payment_mode = sanitize_text_field( $_POST['edd_payment_mode'] );
|
||||
$nonce = sanitize_text_field( $_POST['nonce'] );
|
||||
|
||||
$nonce_verified = wp_verify_nonce( $nonce, 'edd-gateway-selected-' . $payment_mode );
|
||||
|
||||
if ( false !== $nonce_verified ) {
|
||||
// Load the "Express" gateway.
|
||||
if ( 'stripe-prb' === $payment_mode ) {
|
||||
// Remove credit card fields.
|
||||
remove_action( 'edd_stripe_cc_form', 'edds_credit_card_form' );
|
||||
remove_action( 'edd_cc_form', 'edd_get_cc_form' );
|
||||
|
||||
// Hide "Billing Details" which are populated by the Payment Method.
|
||||
add_filter( 'edd_require_billing_address', '__return_true' );
|
||||
remove_filter( 'edd_purchase_form_required_fields', 'edd_stripe_require_zip_and_country' );
|
||||
|
||||
remove_action( 'edd_after_cc_fields', 'edd_stripe_zip_and_country', 9 );
|
||||
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields', 10 );
|
||||
|
||||
// Remove "Update billing address" checkbox. All Payment Requests create
|
||||
// a new source.
|
||||
remove_action( 'edd_cc_billing_top', 'edd_stripe_update_billing_address_field', 10 );
|
||||
|
||||
// Output a Payment Request-specific credit card form (empty).
|
||||
add_action( 'edd_stripe_cc_form', 'edds_prb_cc_form' );
|
||||
|
||||
// Swap purchase button with Payment Request button.
|
||||
add_filter( 'edd_checkout_button_purchase', 'edds_prb_checkout_button_purchase', 10000 );
|
||||
|
||||
/**
|
||||
* Allows further adjustments to made before the "Express Checkout"
|
||||
* gateway is loaded.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
do_action( 'edds_prb_before_purchase_form' );
|
||||
}
|
||||
|
||||
/* This action is documented in easy-digital-downloads/includes/checkout/template.php */
|
||||
do_action( 'edd_purchase_form' );
|
||||
|
||||
// Ensure core callbacks are fired.
|
||||
add_action( 'wp_ajax_edd_load_gateway', 'edd_load_ajax_gateway' );
|
||||
add_action( 'wp_ajax_nopriv_edd_load_gateway', 'edd_load_ajax_gateway' );
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_edd_load_gateway', 'edds_prb_load_gateway', 5 );
|
||||
add_action( 'wp_ajax_nopriv_edd_load_gateway', 'edds_prb_load_gateway', 5 );
|
@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request: Functions
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determines if Payment Requests are enabled.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array|string $context Context the Payment Request Button is being output in.
|
||||
* Default empty, checks if any are enabled.
|
||||
* @return bool
|
||||
*/
|
||||
function edds_prb_is_enabled( $context = array() ) {
|
||||
// Stripe gateway is not active. Disabled.
|
||||
if ( false === edd_is_gateway_active( 'stripe' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gather allowed and enabled contexts.
|
||||
$allowed_contexts = array( 'single', 'archive', 'checkout' );
|
||||
$enabled_contexts = array_keys(
|
||||
(array) edd_get_option( 'stripe_prb', array() )
|
||||
);
|
||||
|
||||
if ( ! is_array( $context ) ) {
|
||||
$context = array( $context );
|
||||
}
|
||||
|
||||
// Nothing particular is being checked for; check if any values are checked.
|
||||
if ( empty( $context ) ) {
|
||||
return count( $enabled_contexts ) > 0;
|
||||
}
|
||||
|
||||
// Passed context is not allowed. Disabled.
|
||||
if ( 0 === count( array_intersect( $context, $allowed_contexts ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Passed context is not enabled in setting. Disabled.
|
||||
if ( 0 === count( array_intersect( $context, $enabled_contexts ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Taxes are enabled. Disabled.
|
||||
$taxes = edd_use_taxes();
|
||||
|
||||
if ( true === $taxes ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recurring is enabled and a trial is in the cart. Disabled.
|
||||
//
|
||||
// Disabling for cart context here to avoid further adjusting the already
|
||||
// complex filtering of active gateways in checkout.php
|
||||
if (
|
||||
function_exists( 'edd_recurring' ) &&
|
||||
edd_recurring()->cart_has_free_trial()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves data for a Payment Request Button for a single Download.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param int $download_id Download ID.
|
||||
* @param false|int $price_id Price ID. Default will be used if not set. Default false.
|
||||
* @param int $quantity Quantity. Default 1.
|
||||
* @return array Payment Request Button data.
|
||||
*/
|
||||
function edds_prb_get_download_data( $download_id, $price_id = false, $quantity = 1 ) {
|
||||
$data = array(
|
||||
'currency' => strtolower( edd_get_currency() ),
|
||||
'country' => strtoupper( edd_get_shop_country() ),
|
||||
'total' => array(),
|
||||
'display-items' => array(),
|
||||
);
|
||||
|
||||
$download = edd_get_download( $download_id );
|
||||
|
||||
// Return early if no Download can be found.
|
||||
if ( ! $download ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Hacky way to ensure we don't display quantity for Recurring
|
||||
// downloads. The quantity field is output incorrectly.
|
||||
//
|
||||
// https://github.com/easydigitaldownloads/edd-recurring/issues/880
|
||||
if ( defined( 'EDD_RECURRING_VERSION' ) ) {
|
||||
$recurring = false;
|
||||
|
||||
if ( false !== $price_id ) {
|
||||
$recurring = edd_recurring()->is_price_recurring( $download_id, $price_id );
|
||||
} else {
|
||||
$recurring = edd_recurring()->is_recurring( $download_id );
|
||||
}
|
||||
|
||||
if ( true === $recurring ) {
|
||||
$quantity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Find price.
|
||||
$variable_pricing = $download->has_variable_prices();
|
||||
$price = 0;
|
||||
|
||||
if ( $variable_pricing ) {
|
||||
if ( false === $price_id ) {
|
||||
$price_id = edd_get_default_variable_price( $download_id );
|
||||
}
|
||||
|
||||
$prices = $download->prices;
|
||||
|
||||
$price = isset( $prices[ $price_id ] )
|
||||
? $prices[ $price_id ]['amount']
|
||||
: false;
|
||||
|
||||
$name = sprintf(
|
||||
'%1$s - %2$s',
|
||||
$download->get_name(),
|
||||
edd_get_price_option_name( $download->ID, $price_id )
|
||||
);
|
||||
} else {
|
||||
$price = $download->price;
|
||||
$name = $download->get_name();
|
||||
}
|
||||
|
||||
if ( false === edds_is_zero_decimal_currency() ) {
|
||||
$price = round( $price * 100 );
|
||||
}
|
||||
|
||||
$price = ( $price * $quantity );
|
||||
|
||||
// Add total.
|
||||
$data['total'] = array(
|
||||
'label' => __( 'Total', 'easy-digital-downloads' ),
|
||||
'amount' => $price,
|
||||
);
|
||||
|
||||
// Add Display items.
|
||||
$has_quantity = edd_item_quantities_enabled() && ! edd_download_quantities_disabled( $download_id );
|
||||
|
||||
$quantity = true === $has_quantity
|
||||
? $quantity
|
||||
: 1;
|
||||
|
||||
$data['display-items'][] = array(
|
||||
'label' => sprintf(
|
||||
'%s%s',
|
||||
strip_tags( $name ),
|
||||
( $quantity > 1 ? sprintf( __( ' × %d', 'easy-digital-downloads' ), $quantity ) : '' )
|
||||
),
|
||||
'amount' => $price,
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves data for a Payment Request Button for the cart.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @return array Payment Request Button data.
|
||||
*/
|
||||
function edds_prb_get_cart_data() {
|
||||
$data = array(
|
||||
'currency' => strtolower( edd_get_currency() ),
|
||||
'country' => strtoupper( edd_get_shop_country() ),
|
||||
'total' => array(),
|
||||
'display-items' => array(),
|
||||
);
|
||||
|
||||
$total = edd_get_cart_total();
|
||||
|
||||
if ( false === edds_is_zero_decimal_currency() ) {
|
||||
$total = round( $total * 100 );
|
||||
}
|
||||
|
||||
// Add total.
|
||||
$data['total'] = array(
|
||||
'label' => __( 'Total', 'easy-digital-downloads' ),
|
||||
'amount' => $total,
|
||||
);
|
||||
|
||||
// Add Display items.
|
||||
$cart_items = edd_get_cart_contents();
|
||||
|
||||
foreach ( $cart_items as $key => $item ) {
|
||||
$has_quantity = edd_item_quantities_enabled() && ! edd_download_quantities_disabled( $item['id'] );
|
||||
|
||||
$quantity = true === $has_quantity
|
||||
? edd_get_cart_item_quantity( $item['id'], $item['options'] )
|
||||
: 1;
|
||||
|
||||
$price = edd_get_cart_item_price( $item['id'], $item['options'] );
|
||||
|
||||
if ( false === edds_is_zero_decimal_currency() ) {
|
||||
$price = round( $price * 100 );
|
||||
}
|
||||
|
||||
$price = ( $price * $quantity );
|
||||
|
||||
$data['display-items'][] = array(
|
||||
'label' => sprintf(
|
||||
'%s%s',
|
||||
strip_tags( edd_get_cart_item_name( $item ) ),
|
||||
( $quantity > 1 ? sprintf( __( ' × %d', 'easy-digital-downloads' ), $quantity ) : '' )
|
||||
),
|
||||
'amount' => $price,
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request Button
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/ajax.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/checkout.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/functions.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/template.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/shortcode.php';
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/apple-pay.php';
|
||||
|
||||
if ( is_admin() ) {
|
||||
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/admin/settings.php';
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request Button: Shortcode
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows `payment-request` to be passed to [purchase-link]
|
||||
* to disable the Payment Request button.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array $out Sanitized shortcode attributes.
|
||||
* @param array $pairs Entire list of supported attributes and their defaults.
|
||||
* @param array $atts User defined attributes in shortcode tag.
|
||||
* @return array Combined and filtered attribute list.
|
||||
*/
|
||||
function edds_prb_shortcode_atts( $out, $pairs, $atts ) {
|
||||
if ( false === edd_is_gateway_active( 'stripe' ) ) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
if ( isset( $atts['payment-request'] ) ) {
|
||||
$out['payment-request'] = $atts['payment-request'];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
add_filter( 'shortcode_atts_purchase_link', 'edds_prb_shortcode_atts', 10, 3 );
|
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment Request Button: Template
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Outputs a Payment Request Button via `edd_get_purchase_link()`
|
||||
* (which is implemented via [purchase_link])
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param int $download_id Current Download ID.
|
||||
* @param array $args Arguments for displaying the purchase link.
|
||||
*/
|
||||
function edds_prb_purchase_link( $download_id, $args ) {
|
||||
// Don't output if context is not enabled.
|
||||
$context = is_singular() && 0 === did_action( 'edd_downloads_list_before' )
|
||||
? 'single'
|
||||
: 'archive';
|
||||
|
||||
if ( false === edds_prb_is_enabled( $context ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't output if the item is free. Stripe won't process < $0.50
|
||||
if ( true === edd_is_free_download( $download_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't output if the item is already in the cart.
|
||||
if ( true === edd_item_in_cart( $download_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't output if `edd_get_purchase_link` is filtered to disable.
|
||||
if (
|
||||
isset( $args['payment-request'] ) &&
|
||||
true !== edds_truthy_to_bool( $args['payment-request'] )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't output if Recurring is enabled, and a free trial is present.
|
||||
// @link https://github.com/easydigitaldownloads/edd-stripe/issues/594
|
||||
if ( function_exists( 'edd_recurring' ) ) {
|
||||
// Don't output if non-variable price has a free trial.
|
||||
if ( edd_recurring()->has_free_trial( $download_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ...or if a variable price options has a free trial.
|
||||
$prices = edd_get_variable_prices( $download_id );
|
||||
|
||||
if ( ! empty( $prices ) ) {
|
||||
foreach ( $prices as $price ) {
|
||||
if ( edd_recurring()->has_free_trial( $download_id, $price['index'] ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't output if it has been filtered off for any reason.
|
||||
$enabled = true;
|
||||
|
||||
/**
|
||||
* Filters the output of Payment Request Button in purchase links.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param bool $enabled If the Payment Request Button is enabled.
|
||||
* @param int $download_id Current Download ID.
|
||||
* @param array $args Purchase link arguments.
|
||||
*/
|
||||
$enabled = apply_filters( 'edds_prb_purchase_link_enabled', $enabled, $download_id, $args );
|
||||
|
||||
if ( true !== $enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
static $instance_id = 0;
|
||||
|
||||
echo edds_get_prb_markup(
|
||||
edds_prb_get_download_data( $download_id ),
|
||||
array(
|
||||
'id' => sprintf(
|
||||
'edds-prb-download-%d-%d',
|
||||
$download_id,
|
||||
$instance_id
|
||||
),
|
||||
'classes' => array(
|
||||
'edds-prb--download',
|
||||
),
|
||||
)
|
||||
); // WPCS: XSS okay.
|
||||
|
||||
// Shim the Checkout processing nonce.
|
||||
wp_nonce_field( 'edd-process-checkout', 'edd-process-checkout-nonce', false );
|
||||
echo edds_get_tokenizer_input( $download_id );
|
||||
|
||||
$instance_id++;
|
||||
}
|
||||
add_action( 'edd_purchase_link_top', 'edds_prb_purchase_link', 20, 2 );
|
||||
|
||||
/**
|
||||
* Outputs a Payment Request Button on the Checkout.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edds_prb_checkout_button_purchase( $button ) {
|
||||
// Do nothing if Payment Requests are not enabled.
|
||||
if ( false === edds_prb_is_enabled( 'checkout' ) ) {
|
||||
return $button;
|
||||
}
|
||||
|
||||
$tokenizer = edds_get_tokenizer_input();
|
||||
$errors = '<div id="edds-prb-error-wrap"></div>';
|
||||
|
||||
$button = edds_get_prb_markup(
|
||||
edds_prb_get_cart_data(),
|
||||
array(
|
||||
'id' => 'edds-prb-checkout',
|
||||
'classes' => array(
|
||||
'edds-prb--checkout',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return $tokenizer . $errors . $button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves HTML used to mount a Payment Request Button.
|
||||
*
|
||||
* @since 2.8.0
|
||||
* @see edds_prb_get_download_data()
|
||||
* @link https://stripe.com/docs/js/appendix/payment_item_object
|
||||
*
|
||||
* @param PaymentItem[] $data {
|
||||
* PaymentItems.
|
||||
*
|
||||
* @type int $amount The amount in the currency's subunit.
|
||||
* @type string $label A name the browser shows the customer in the payment interface.
|
||||
* }
|
||||
* @param array $args {
|
||||
* Mount arguments.
|
||||
*
|
||||
* @type string $id HTML ID attribute.
|
||||
* @type array $classes HTML classes.
|
||||
* }
|
||||
* @return string
|
||||
*/
|
||||
function edds_get_prb_markup( $data, $args = array() ) {
|
||||
$defaults = array(
|
||||
'id' => '',
|
||||
'classes' => array(),
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
// ID/Class
|
||||
$id = $args['id'];
|
||||
$class = implode(
|
||||
' ',
|
||||
array_merge(
|
||||
$args['classes'],
|
||||
array(
|
||||
'edds-prb',
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Data
|
||||
$_data = array();
|
||||
|
||||
foreach ( $data as $key => $value ) {
|
||||
$_data[] = sprintf(
|
||||
'data-%s="%s"',
|
||||
esc_attr( $key ),
|
||||
esc_attr( is_array( $value ) ? wp_json_encode( $value ) : $value )
|
||||
);
|
||||
}
|
||||
|
||||
$_data = implode( ' ', $_data );
|
||||
|
||||
edd_stripe_js( true );
|
||||
edd_stripe_css( true );
|
||||
|
||||
return sprintf(
|
||||
'<div id="%1$s" class="%2$s" %3$s>
|
||||
<div class="edds-prb__button"></div>
|
||||
<div class="edds-prb__or">%4$s</div>
|
||||
</div>',
|
||||
esc_attr( $id ),
|
||||
esc_attr( $class ),
|
||||
$_data,
|
||||
esc_html_x( 'or', 'payment request button divider', 'easy-digital-downloads' )
|
||||
);
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Payment receipt.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Output a Payment authorization form in the Payment Receipt.
|
||||
*
|
||||
* @param WP_Post $payment Payment.
|
||||
*/
|
||||
function edds_payment_receipt_authorize_payment_form( $payment ) {
|
||||
// EDD 3.0 compat.
|
||||
if ( is_a( $payment, 'WP_Post' ) ) {
|
||||
$payment = edd_get_payment( $payment->ID );
|
||||
}
|
||||
|
||||
$customer_id = $payment->get_meta( '_edds_stripe_customer_id' );
|
||||
$payment_intent_id = $payment->get_meta( '_edds_stripe_payment_intent_id' );
|
||||
|
||||
if ( empty( $customer_id ) || empty( $payment_intent_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'preapproval_pending' !== $payment->status ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$payment_intent = edds_api_request( 'PaymentIntent', 'retrieve', $payment_intent_id );
|
||||
|
||||
// Enqueue core scripts.
|
||||
add_filter( 'edd_is_checkout', '__return_true' );
|
||||
|
||||
edd_load_scripts();
|
||||
|
||||
remove_filter( 'edd_is_checkout', '__return_true' );
|
||||
|
||||
edd_stripe_js( true );
|
||||
edd_stripe_css( true );
|
||||
?>
|
||||
|
||||
<form
|
||||
id="edds-update-payment-method"
|
||||
data-payment-intent="<?php echo esc_attr( $payment_intent->id ); ?>"
|
||||
<?php if ( isset( $payment_intent->last_payment_error ) && isset( $payment_intent->last_payment_error->payment_method ) ) : ?>
|
||||
data-payment-method="<?php echo esc_attr( $payment_intent->last_payment_error->payment_method->id ); ?>"
|
||||
<?php endif; ?>
|
||||
>
|
||||
<h3>Authorize Payment</h3>
|
||||
<p><?php esc_html_e( 'To finalize your preapproved purchase, please confirm your payment method.', 'easy-digital-downloads' ); ?></p>
|
||||
|
||||
<div id="edd_checkout_form_wrap">
|
||||
<?php
|
||||
/** This filter is documented in easydigitaldownloads/includes/checkout/template.php */
|
||||
do_action( 'edd_stripe_cc_form' );
|
||||
?>
|
||||
|
||||
<p>
|
||||
<input
|
||||
id="edds-update-payment-method-submit"
|
||||
type="submit"
|
||||
data-loading="<?php echo esc_attr( 'Please Wait…', 'edds' ); ?>"
|
||||
data-submit="<?php echo esc_attr( 'Authorize Payment', 'edds' ); ?>"
|
||||
value="<?php echo esc_attr( 'Authorize Payment', 'edds' ); ?>"
|
||||
class="button edd-button"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<div id="edds-update-payment-method-errors"></div>
|
||||
|
||||
<?php
|
||||
wp_nonce_field(
|
||||
'edds-complete-payment-authorization',
|
||||
'edds-complete-payment-authorization'
|
||||
);
|
||||
?>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
}
|
||||
add_action( 'edd_payment_receipt_after_table', 'edds_payment_receipt_authorize_payment_form' );
|
@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Load our javascript
|
||||
*
|
||||
* The Stripe JS is by default, loaded on every page as suggested by Stripe. This can be overridden by using the Restrict Stripe Assets
|
||||
* setting within the admin, and the Stripe Javascript resources will only be loaded when necessary.
|
||||
* @link https://stripe.com/docs/web/setup
|
||||
*
|
||||
* The custom Javascript for EDD is loaded if the page is checkout. If checkout, the function is called directly with
|
||||
* `true` set for the `$force_load_scripts` argument.
|
||||
*
|
||||
* @access public
|
||||
* @since 1.0
|
||||
*
|
||||
* @param bool $force_load_scripts Allows registering our Javascript files on pages other than is_checkout().
|
||||
* This argument allows the `edd_stripe_js` function to be called directly, outside of
|
||||
* the context of checkout, such as the card management or update subscription payment method
|
||||
* UIs. Sending in 'true' will ensure that the Javascript resources are enqueued when you need them.
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_js( $force_load_scripts = false ) {
|
||||
if ( false === edds_is_gateway_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( function_exists( 'edd_is_checkout' ) ) {
|
||||
|
||||
$publishable_key = NULL;
|
||||
|
||||
if ( edd_is_test_mode() ) {
|
||||
$publishable_key = edd_get_option( 'test_publishable_key', '' );
|
||||
} else {
|
||||
$publishable_key = edd_get_option( 'live_publishable_key', '' );
|
||||
}
|
||||
|
||||
wp_register_script(
|
||||
'sandhills-stripe-js-v3',
|
||||
'https://js.stripe.com/v3/',
|
||||
array(),
|
||||
'v3'
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'edd-stripe-js',
|
||||
EDDSTRIPE_PLUGIN_URL . 'assets/js/build/app.min.js',
|
||||
array(
|
||||
'sandhills-stripe-js-v3',
|
||||
'jquery',
|
||||
'edd-ajax'
|
||||
),
|
||||
EDD_STRIPE_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
$is_checkout = edd_is_checkout() && 0 < edd_get_cart_total();
|
||||
$restrict_assets = edd_get_option( 'stripe_restrict_assets', false );
|
||||
|
||||
if ( $is_checkout || $force_load_scripts || false === $restrict_assets ) {
|
||||
wp_enqueue_script( 'sandhills-stripe-js-v3' );
|
||||
}
|
||||
|
||||
if ( $is_checkout || $force_load_scripts ) {
|
||||
wp_enqueue_script( 'edd-stripe-js' );
|
||||
wp_enqueue_script( 'jQuery.payment' );
|
||||
|
||||
$stripe_vars = apply_filters( 'edd_stripe_js_vars', array(
|
||||
'publishable_key' => trim( $publishable_key ),
|
||||
'is_ajaxed' => edd_is_ajax_enabled() ? 'true' : 'false',
|
||||
'currency' => edd_get_currency(),
|
||||
// @todo Replace with country code derived from Stripe Account information if available.
|
||||
// @link https://github.com/easydigitaldownloads/edd-stripe/issues/654
|
||||
'country' => edd_get_option( 'base_country', 'US' ),
|
||||
'locale' => edds_get_stripe_checkout_locale(),
|
||||
'is_zero_decimal' => edds_is_zero_decimal_currency() ? 'true' : 'false',
|
||||
'checkout' => edd_get_option( 'stripe_checkout' ) ? 'true' : 'false',
|
||||
'store_name' => get_bloginfo( 'name' ),
|
||||
'alipay' => edd_get_option( 'stripe_alipay' ) ? 'true' : 'false',
|
||||
'submit_text' => edd_get_option( 'stripe_checkout_button_text', __( 'Next', 'easy-digital-downloads' ) ),
|
||||
'image' => edd_get_option( 'stripe_checkout_image' ),
|
||||
'zipcode' => edd_get_option( 'stripe_checkout_zip_code', false ) ? 'true' : 'false',
|
||||
'billing_address' => edd_get_option( 'stripe_checkout_billing', false ) ? 'true' : 'false',
|
||||
'remember_me' => edd_get_option( 'stripe_checkout_remember', false ) ? 'true' : 'false',
|
||||
'no_key_error' => __( 'Stripe publishable key missing. Please enter your publishable key in Settings.', 'easy-digital-downloads' ),
|
||||
'checkout_required_fields_error' => __( 'Please fill out all required fields to continue your purchase.', 'easy-digital-downloads' ),
|
||||
'checkout_agree_to_terms' => __( 'Please agree to the terms to complete your purchase.', 'easy-digital-downloads' ),
|
||||
'checkout_agree_to_privacy' => __( 'Please agree to the privacy policy to complete your purchase.', 'easy-digital-downloads' ),
|
||||
'generic_error' => __( 'Unable to complete your request. Please try again.', 'easy-digital-downloads' ),
|
||||
'successPageUri' => edd_get_success_page_uri(),
|
||||
'failurePageUri' => edd_get_failed_transaction_uri(),
|
||||
'elementsOptions' => edds_get_stripe_elements_options(),
|
||||
'elementsSplitFields' => '1' === edd_get_option( 'stripe_split_payment_fields', false ) ? 'true' : 'false',
|
||||
'isTestMode' => edd_is_test_mode() ? 'true' : 'false',
|
||||
'checkoutHasPaymentRequest' => edds_prb_is_enabled( 'checkout' ) ? 'true' : 'false',
|
||||
) );
|
||||
|
||||
wp_localize_script( 'edd-stripe-js', 'edd_stripe_vars', $stripe_vars );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'edd_stripe_js', 100 );
|
||||
|
||||
function edd_stripe_css( $force_load_scripts = false ) {
|
||||
if ( false === edds_is_gateway_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( edd_is_checkout() || $force_load_scripts ) {
|
||||
$deps = array( 'edd-styles' );
|
||||
|
||||
if ( ! wp_script_is( 'edd-styles', 'enqueued' ) ) {
|
||||
$deps = array();
|
||||
}
|
||||
|
||||
wp_register_style( 'edd-stripe', EDDSTRIPE_PLUGIN_URL . 'assets/css/build/app.min.css', $deps, EDD_STRIPE_VERSION );
|
||||
wp_enqueue_style( 'edd-stripe' );
|
||||
}
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'edd_stripe_css', 100 );
|
||||
|
||||
/**
|
||||
* Load our admin javascript
|
||||
*
|
||||
* @access public
|
||||
* @since 1.8
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_admin_js( $payment_id = 0 ) {
|
||||
|
||||
// Bail if on EDD 3.0+.
|
||||
if ( function_exists( 'edd_get_order' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( 'stripe' !== edd_get_payment_gateway( $payment_id ) ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
jQuery(document).ready(function($) {
|
||||
$('select[name=edd-payment-status]').change(function() {
|
||||
|
||||
if( 'refunded' == $(this).val() ) {
|
||||
|
||||
// Localize refund label
|
||||
var edd_stripe_refund_charge_label = "<?php echo esc_js( __( 'Refund Charge in Stripe', 'easy-digital-downloads' ) ); ?>";
|
||||
|
||||
$(this).parent().parent().append( '<input type="checkbox" id="edd_refund_in_stripe" name="edd_refund_in_stripe" value="1" style="margin-top: 0;" />' );
|
||||
$(this).parent().parent().append( '<label for="edd_refund_in_stripe">' + edd_stripe_refund_charge_label + '</label>' );
|
||||
|
||||
} else {
|
||||
|
||||
$('#edd_refund_in_stripe').remove();
|
||||
$('label[for="edd_refund_in_stripe"]').remove();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
|
||||
}
|
||||
add_action( 'edd_view_order_details_before', 'edd_stripe_admin_js', 100 );
|
||||
|
||||
/**
|
||||
* Loads the javascript for the Stripe Connect functionality in the settings page.
|
||||
*
|
||||
* @param string $hook The current admin page.
|
||||
*/
|
||||
function edd_stripe_connect_admin_script( $hook ) {
|
||||
|
||||
if( 'download_page_edd-settings' !== $hook ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style( 'edd-stripe-admin-styles', EDDSTRIPE_PLUGIN_URL . 'assets/css/build/admin.min.css', array(), EDD_STRIPE_VERSION );
|
||||
|
||||
wp_enqueue_script( 'edd-stripe-admin-scripts', EDDSTRIPE_PLUGIN_URL . 'assets/js/build/admin.min.js', array( 'jquery' ), EDD_STRIPE_VERSION );
|
||||
|
||||
$test_key = edd_get_option( 'test_publishable_key' );
|
||||
$live_key = edd_get_option( 'live_publishable_key' );
|
||||
|
||||
wp_localize_script(
|
||||
'edd-stripe-admin-scripts',
|
||||
'edd_stripe_admin',
|
||||
array(
|
||||
'stripe_enabled' => array_key_exists( 'stripe', edd_get_enabled_payment_gateways() ),
|
||||
'test_mode' => (int) edd_is_test_mode(),
|
||||
'test_key_exists' => ! empty( $test_key ) ? 'true' : 'false',
|
||||
'live_key_exists' => ! empty( $live_key ) ? 'true' : 'false',
|
||||
'ajaxurl' => esc_url( admin_url( 'admin-ajax.php' ) ),
|
||||
)
|
||||
);
|
||||
}
|
||||
add_action( 'admin_enqueue_scripts', 'edd_stripe_connect_admin_script' );
|
@ -0,0 +1,864 @@
|
||||
<?php
|
||||
/**
|
||||
* Add an errors div
|
||||
*
|
||||
* @since 1.0
|
||||
* @return void
|
||||
*/
|
||||
function edds_add_stripe_errors() {
|
||||
echo '<div id="edd-stripe-payment-errors"></div>';
|
||||
}
|
||||
add_action( 'edd_after_cc_fields', 'edds_add_stripe_errors', 999 );
|
||||
|
||||
/**
|
||||
* Stripe uses it's own credit card form because the card details are tokenized.
|
||||
*
|
||||
* We don't want the name attributes to be present on the fields in order to prevent them from getting posted to the server
|
||||
*
|
||||
* @since 1.7.5
|
||||
* @return void
|
||||
*/
|
||||
function edds_credit_card_form( $echo = true ) {
|
||||
|
||||
global $edd_options;
|
||||
|
||||
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
|
||||
edd_set_error( 'edd_stripe_error_limit', __( 'We are unable to process your payment at this time, please try again later or contact support.', 'easy-digital-downloads' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start(); ?>
|
||||
|
||||
<?php if ( ! wp_script_is ( 'edd-stripe-js' ) ) : ?>
|
||||
<?php edd_stripe_js( true ); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php do_action( 'edd_before_cc_fields' ); ?>
|
||||
|
||||
<fieldset id="edd_cc_fields" class="edd-do-validate">
|
||||
<legend><?php _e( 'Credit Card Info', 'easy-digital-downloads' ); ?></legend>
|
||||
<?php if( is_ssl() ) : ?>
|
||||
<div id="edd_secure_site_wrapper">
|
||||
<span class="padlock">
|
||||
<?php
|
||||
if ( function_exists( 'edd_get_payment_icon' ) ) {
|
||||
echo edd_get_payment_icon(
|
||||
array(
|
||||
'icon' => 'lock',
|
||||
'width' => 18,
|
||||
'height' => 28,
|
||||
'classes' => array(
|
||||
'edd-icon',
|
||||
'edd-icon-lock',
|
||||
),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
?>
|
||||
<svg class="edd-icon edd-icon-lock" xmlns="http://www.w3.org/2000/svg" width="18" height="28" viewBox="0 0 18 28" aria-hidden="true">
|
||||
<path d="M5 12h8V9c0-2.203-1.797-4-4-4S5 6.797 5 9v3zm13 1.5v9c0 .828-.672 1.5-1.5 1.5h-15C.672 24 0 23.328 0 22.5v-9c0-.828.672-1.5 1.5-1.5H2V9c0-3.844 3.156-7 7-7s7 3.156 7 7v3h.5c.828 0 1.5.672 1.5 1.5z"/>
|
||||
</svg>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</span>
|
||||
<span><?php _e( 'This is a secure SSL encrypted payment.', 'easy-digital-downloads' ); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
?>
|
||||
<?php if ( ! empty( $existing_cards ) ) { edd_stripe_existing_card_field_radio( get_current_user_id() ); } ?>
|
||||
|
||||
<div class="edd-stripe-new-card" <?php if ( ! empty( $existing_cards ) ) { echo 'style="display: none;"'; } ?>>
|
||||
<?php do_action( 'edd_stripe_new_card_form' ); ?>
|
||||
<?php do_action( 'edd_after_cc_expiration' ); ?>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
<?php
|
||||
echo edds_get_tokenizer_input();
|
||||
|
||||
do_action( 'edd_after_cc_fields' );
|
||||
|
||||
$form = ob_get_clean();
|
||||
|
||||
if ( false !== $echo ) {
|
||||
echo $form;
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
add_action( 'edd_stripe_cc_form', 'edds_credit_card_form' );
|
||||
|
||||
/**
|
||||
* Display the markup for the Stripe new card form
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_new_card_form() {
|
||||
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
|
||||
edd_set_error( 'edd_stripe_error_limit', __( 'Adding new payment methods is currently unavailable.', 'easy-digital-downloads' ) );
|
||||
edd_print_errors();
|
||||
return;
|
||||
}
|
||||
|
||||
$split = edd_get_option( 'stripe_split_payment_fields', false );
|
||||
?>
|
||||
|
||||
<p id="edd-card-name-wrap">
|
||||
<label for="card_name" class="edd-label">
|
||||
<?php esc_html_e( 'Name on the Card', 'easy-digital-downloads' ); ?>
|
||||
<span class="edd-required-indicator">*</span>
|
||||
</label>
|
||||
<span class="edd-description"><?php esc_html_e( 'The name printed on the front of your credit card.', 'easy-digital-downloads' ); ?></span>
|
||||
<input type="text" name="card_name" id="card_name" class="card-name edd-input required" placeholder="<?php esc_attr_e( 'Card name', 'easy-digital-downloads' ); ?>" autocomplete="cc-name" />
|
||||
</p>
|
||||
|
||||
<div id="edd-card-wrap">
|
||||
<label for="edd-card-element" class="edd-label">
|
||||
<?php
|
||||
if ( '1' === $split ) :
|
||||
esc_html_e( 'Credit Card Number', 'easy-digital-downloads' );
|
||||
else :
|
||||
esc_html_e( 'Credit Card', 'easy-digital-downloads' );
|
||||
endif;
|
||||
?>
|
||||
<span class="edd-required-indicator">*</span>
|
||||
</label>
|
||||
|
||||
<div id="edd-stripe-card-element-wrapper">
|
||||
<?php if ( '1' === $split ) : ?>
|
||||
<span class="card-type"></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="edd-stripe-card-element" class="edd-stripe-card-element"></div>
|
||||
</div>
|
||||
|
||||
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
|
||||
</div>
|
||||
|
||||
<?php if ( '1' === $split ) : ?>
|
||||
|
||||
<div id="edd-card-details-wrap">
|
||||
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
|
||||
|
||||
<div id="edd-card-exp-wrap">
|
||||
<label for="edd-card-exp-element" class="edd-label">
|
||||
<?php esc_html_e( 'Expiration', 'easy-digital-downloads' ); ?>
|
||||
<span class="edd-required-indicator">*</span>
|
||||
</label>
|
||||
|
||||
<div id="edd-stripe-card-exp-element" class="edd-stripe-card-exp-element"></div>
|
||||
</div>
|
||||
|
||||
<div id="edd-card-cvv-wrap">
|
||||
<label for="edd-card-exp-element" class="edd-label">
|
||||
<?php esc_html_e( 'CVC', 'easy-digital-downloads' ); ?>
|
||||
<span class="edd-required-indicator">*</span>
|
||||
</label>
|
||||
|
||||
<div id="edd-stripe-card-cvc-element" class="edd-stripe-card-cvc-element"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="edd-stripe-card-errors" role="alert"></div>
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Allow output of extra content before the credit card expiration field.
|
||||
*
|
||||
* This content no longer appears before the credit card expiration field
|
||||
* with the introduction of Stripe Elements.
|
||||
*
|
||||
* @deprecated 2.7
|
||||
* @since unknown
|
||||
*/
|
||||
do_action( 'edd_before_cc_expiration' );
|
||||
}
|
||||
add_action( 'edd_stripe_new_card_form', 'edd_stripe_new_card_form' );
|
||||
|
||||
/**
|
||||
* Show the checkbox for updating the billing information on an existing Stripe card
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_update_billing_address_field() {
|
||||
$payment_mode = strtolower( edd_get_chosen_gateway() );
|
||||
if ( edd_is_checkout() && 'stripe' !== $payment_mode ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
if ( empty( $existing_cards ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! did_action( 'edd_stripe_cc_form' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$default_card = false;
|
||||
|
||||
foreach ( $existing_cards as $existing_card ) {
|
||||
if ( $existing_card['default'] ) {
|
||||
$default_card = $existing_card['source'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<p class="edd-stripe-update-billing-address-current">
|
||||
<?php
|
||||
if ( $default_card ) :
|
||||
$address_fields = array(
|
||||
'line1' => isset( $default_card->address_line1 ) ? $default_card->address_line1 : null,
|
||||
'line2' => isset( $default_card->address_line2 ) ? $default_card->address_line2 : null,
|
||||
'city' => isset( $default_card->address_city ) ? $default_card->address_city : null,
|
||||
'state' => isset( $default_card->address_state ) ? $default_card->address_state : null,
|
||||
'zip' => isset( $default_card->address_zip ) ? $default_card->address_zip : null,
|
||||
'country' => isset( $default_card->address_country ) ? $default_card->address_country : null,
|
||||
);
|
||||
|
||||
$address_fields = array_filter( $address_fields );
|
||||
|
||||
echo esc_html( implode( ', ', $address_fields ) );
|
||||
endif;
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p class="edd-stripe-update-billing-address-wrapper">
|
||||
<input type="checkbox" name="edd_stripe_update_billing_address" id="edd-stripe-update-billing-address" value="1" />
|
||||
<label for="edd-stripe-update-billing-address">
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Card type. %2$s Card last 4. */
|
||||
__( 'Update card billing address for %1$s •••• %2$s', 'easy-digital-downloads' ),
|
||||
'<span class="edd-stripe-update-billing-address-brand">' . ( $default_card ? $default_card->brand : '' ) . '</span>',
|
||||
'<span class="edd-stripe-update-billing-address-last4">' . ( $default_card ? $default_card->last4 : '' ) . '</span>'
|
||||
),
|
||||
array(
|
||||
'strong' => true,
|
||||
'span' => array(
|
||||
'class' => true,
|
||||
),
|
||||
)
|
||||
);
|
||||
?>
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
add_action( 'edd_cc_billing_top', 'edd_stripe_update_billing_address_field', 10 );
|
||||
|
||||
/**
|
||||
* Display a radio list of existing cards on file for a user ID
|
||||
*
|
||||
* @since 2.6
|
||||
* @param int $user_id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_existing_card_field_radio( $user_id = 0 ) {
|
||||
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
|
||||
edd_set_error( 'edd_stripe_error_limit', __( 'We are unable to process your payment at this time, please try again later or contacts support.', 'easy-digital-downloads' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Can't use just edd_is_checkout() because this could happen in an AJAX request.
|
||||
$is_checkout = edd_is_checkout() || ( isset( $_REQUEST['action'] ) && 'edd_load_gateway' === $_REQUEST['action'] );
|
||||
|
||||
edd_stripe_css( true );
|
||||
$existing_cards = edd_stripe_get_existing_cards( $user_id );
|
||||
if ( ! empty( $existing_cards ) ) : ?>
|
||||
<div class="edd-stripe-card-selector edd-card-selector-radio">
|
||||
<?php foreach ( $existing_cards as $card ) : ?>
|
||||
<?php $source = $card['source']; ?>
|
||||
<div class="edd-stripe-card-radio-item existing-card-wrapper <?php if ( $card['default'] ) { echo ' selected'; } ?>">
|
||||
<input
|
||||
type="hidden"
|
||||
id="<?php echo esc_attr( $source->id ); ?>-billing-details"
|
||||
data-address_city="<?php echo esc_attr( $source->address_city ); ?>"
|
||||
data-address_country="<?php echo esc_attr( $source->address_country ); ?>"
|
||||
data-address_line1="<?php echo esc_attr( $source->address_line1 ); ?>"
|
||||
data-address_line2="<?php echo esc_attr( $source->address_line2 ); ?>"
|
||||
data-address_state="<?php echo esc_attr( $source->address_state ); ?>"
|
||||
data-address_zip="<?php echo esc_attr( $source->address_zip ); ?>"
|
||||
data-brand="<?php echo esc_attr( $source->brand ); ?>"
|
||||
data-last4="<?php echo esc_attr( $source->last4 ); ?>"
|
||||
/>
|
||||
<label for="<?php echo esc_attr( $source->id ); ?>">
|
||||
<input <?php checked( true, $card['default'], true ); ?> type="radio" id="<?php echo esc_attr( $source->id ); ?>" name="edd_stripe_existing_card" value="<?php echo esc_attr( $source->id ); ?>" class="edd-stripe-existing-card">
|
||||
<span class="card-label">
|
||||
<span class="card-data">
|
||||
<span class="card-name-number">
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
/* translators: %1$s Card type. %2$s Card last 4. */
|
||||
__( '%1$s •••• %2$s', 'easy-digital-downloads' ),
|
||||
'<span class="card-brand">' . $source->brand . '</span>',
|
||||
'<span class="card-last-4">' . $source->last4 . '</span>'
|
||||
),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => true,
|
||||
),
|
||||
)
|
||||
);
|
||||
?>
|
||||
</span>
|
||||
<small class="card-expires-on">
|
||||
<span class="default-card-sep"><?php echo ' '; ?></span>
|
||||
<span class="card-expiration">
|
||||
<?php echo $source->exp_month . '/' . $source->exp_year; ?>
|
||||
</span>
|
||||
</small>
|
||||
</span>
|
||||
<?php
|
||||
$current = strtotime( date( 'm/Y' ) );
|
||||
$exp_date = strtotime( $source->exp_month . '/' . $source->exp_year );
|
||||
if ( $exp_date < $current ) :
|
||||
?>
|
||||
<span class="card-expired">
|
||||
<?php _e( 'Expired', 'easy-digital-downloads' ); ?>
|
||||
</span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<div class="edd-stripe-card-radio-item new-card-wrapper">
|
||||
<label for="edd-stripe-add-new">
|
||||
<input type="radio" id="edd-stripe-add-new" class="edd-stripe-existing-card" name="edd_stripe_existing_card" value="new" />
|
||||
<span class="add-new-card"><?php _e( 'Add New Card', 'easy-digital-downloads' ); ?></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the management interface for a user's Stripe card
|
||||
*
|
||||
* @since 2.6
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_manage_cards() {
|
||||
if ( false === edds_is_gateway_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$enabled = edd_stripe_existing_cards_enabled();
|
||||
if ( ! $enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
|
||||
if ( empty( $stripe_customer_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
|
||||
edd_set_error( 'edd_stripe_error_limit', __( 'Payment method management is currently unavailable.', 'easy-digital-downloads' ) );
|
||||
edd_print_errors();
|
||||
return;
|
||||
}
|
||||
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
|
||||
edd_stripe_css( true );
|
||||
edd_stripe_js( true );
|
||||
$display = edd_get_option( 'stripe_billing_fields', 'full' );
|
||||
?>
|
||||
<div id="edd-stripe-manage-cards">
|
||||
<fieldset>
|
||||
<legend><?php _e( 'Manage Payment Methods', 'easy-digital-downloads' ); ?></legend>
|
||||
<input type="hidden" id="stripe-update-card-user_id" name="stripe-update-user-id" value="<?php echo esc_attr( get_current_user_id() ); ?>" />
|
||||
<?php if ( ! empty( $existing_cards ) ) : ?>
|
||||
<?php foreach( $existing_cards as $card ) : ?>
|
||||
<?php $source = $card['source']; ?>
|
||||
<div id="<?php echo esc_attr( $source->id ); ?>_card_item" class="edd-stripe-card-item">
|
||||
<span class="card-details">
|
||||
<?php
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
__( '%1$s •••• %2$s', 'easy-digital-downloads' ),
|
||||
'<span class="card-brand">' . esc_html( $source->brand ) . '</span>',
|
||||
'<span class="card-last-4">' . esc_html( $source->last4 ) . '</span>'
|
||||
),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => true,
|
||||
),
|
||||
)
|
||||
);
|
||||
?>
|
||||
|
||||
<?php if ( $card['default'] ) { ?>
|
||||
<span class="default-card-sep"><?php echo '— '; ?></span>
|
||||
<span class="card-is-default"><?php _e( 'Default', 'easy-digital-downloads'); ?></span>
|
||||
<?php } ?>
|
||||
</span>
|
||||
|
||||
<span class="card-meta">
|
||||
<span class="card-expiration"><span class="card-expiration-label"><?php _e( 'Expires', 'easy-digital-downloads' ); ?>: </span><span class="card-expiration-date"><?php echo $source->exp_month; ?>/<?php echo $source->exp_year; ?></span></span>
|
||||
<span class="card-address">
|
||||
<?php
|
||||
$address_fields = array(
|
||||
'line1' => isset( $source->address_line1 ) ? $source->address_line1 : '',
|
||||
'zip' => isset( $source->address_zip ) ? $source->address_zip : '',
|
||||
'country' => isset( $source->address_country ) ? $source->address_country : '',
|
||||
);
|
||||
|
||||
echo esc_html( implode( ' ', $address_fields ) );
|
||||
?>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span id="<?php echo esc_attr( $source->id ); ?>-card-actions" class="card-actions">
|
||||
<span class="card-update">
|
||||
<a href="#" class="edd-stripe-update-card" data-source="<?php echo esc_attr( $source->id ); ?>"><?php _e( 'Update', 'easy-digital-downloads' ); ?></a>
|
||||
</span>
|
||||
|
||||
<?php if ( ! $card['default'] ) : ?>
|
||||
|
|
||||
<span class="card-set-as-default">
|
||||
<a href="#" class="edd-stripe-default-card" data-source="<?php echo esc_attr( $source->id ); ?>"><?php _e( 'Set as Default', 'easy-digital-downloads' ); ?></a>
|
||||
</span>
|
||||
<?php
|
||||
endif;
|
||||
|
||||
$can_delete = apply_filters( 'edd_stripe_can_delete_card', true, $card, $existing_cards );
|
||||
if ( $can_delete ) :
|
||||
?>
|
||||
|
|
||||
<span class="card-delete">
|
||||
<a href="#" class="edd-stripe-delete-card delete" data-source="<?php echo esc_attr( $source->id ); ?>"><?php _e( 'Delete', 'easy-digital-downloads' ); ?></a>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<span style="display: none;" class="edd-loading-ajax edd-loading"></span>
|
||||
</span>
|
||||
|
||||
<form id="<?php echo esc_attr( $source->id ); ?>-update-form" class="card-update-form" data-source="<?php echo esc_attr( $source->id ); ?>">
|
||||
<label><?php _e( 'Billing Details', 'easy-digital-downloads' ); ?></label>
|
||||
|
||||
<div class="card-address-fields">
|
||||
<p class="edds-card-address-field edds-card-address-field--address1">
|
||||
<?php
|
||||
echo EDD()->html->text( array(
|
||||
'id' => sprintf( 'edds_address_line1_%1$s', $source->id ),
|
||||
'value' => sanitize_text_field( isset( $source->address_line1 ) ? $source->address_line1 : '' ),
|
||||
'label' => esc_html__( 'Address Line 1', 'easy-digital-downloads' ),
|
||||
'name' => 'address_line1',
|
||||
'class' => 'card-update-field address_line1 text edd-input',
|
||||
'data' => array(
|
||||
'key' => 'address_line1',
|
||||
)
|
||||
) );
|
||||
?>
|
||||
</p>
|
||||
<p class="edds-card-address-field edds-card-address-field--address2">
|
||||
<?php
|
||||
echo EDD()->html->text( array(
|
||||
'id' => sprintf( 'edds_address_line2_%1$s', $source->id ),
|
||||
'value' => sanitize_text_field( isset( $source->address_line2 ) ? $source->address_line2 : '' ),
|
||||
'label' => esc_html__( 'Address Line 2', 'easy-digital-downloads' ),
|
||||
'name' => 'address_line2',
|
||||
'class' => 'card-update-field address_line2 text edd-input',
|
||||
'data' => array(
|
||||
'key' => 'address_line2',
|
||||
)
|
||||
) );
|
||||
?>
|
||||
</p>
|
||||
<p class="edds-card-address-field edds-card-address-field--city">
|
||||
<?php
|
||||
echo EDD()->html->text( array(
|
||||
'id' => sprintf( 'edds_address_city_%1$s', $source->id ),
|
||||
'value' => sanitize_text_field( isset( $source->address_city ) ? $source->address_city : '' ),
|
||||
'label' => esc_html__( 'City', 'easy-digital-downloads' ),
|
||||
'name' => 'address_city',
|
||||
'class' => 'card-update-field address_city text edd-input',
|
||||
'data' => array(
|
||||
'key' => 'address_city',
|
||||
)
|
||||
) );
|
||||
?>
|
||||
</p>
|
||||
<p class="edds-card-address-field edds-card-address-field--zip">
|
||||
<?php
|
||||
echo EDD()->html->text( array(
|
||||
'id' => sprintf( 'edds_address_zip_%1$s', $source->id ),
|
||||
'value' => sanitize_text_field( isset( $source->address_zip ) ? $source->address_zip : '' ),
|
||||
'label' => esc_html__( 'ZIP Code', 'easy-digital-downloads' ),
|
||||
'name' => 'address_zip',
|
||||
'class' => 'card-update-field address_zip text edd-input',
|
||||
'data' => array(
|
||||
'key' => 'address_zip',
|
||||
)
|
||||
) );
|
||||
?>
|
||||
</p>
|
||||
<p class="edds-card-address-field edds-card-address-field--country">
|
||||
<label for="<?php echo esc_attr( sprintf( 'edds_address_country_%1$s', $source->id ) ); ?>">
|
||||
<?php esc_html_e( 'Country', 'easy-digital-downloads' ); ?>
|
||||
</label>
|
||||
|
||||
<?php
|
||||
$countries = array_filter( edd_get_country_list() );
|
||||
$country = isset( $source->address_country ) ? $source->address_country : edd_get_shop_country();
|
||||
echo EDD()->html->select( array(
|
||||
'id' => sprintf( 'edds_address_country_%1$s', $source->id ),
|
||||
'name' => 'address_country',
|
||||
'label' => esc_html__( 'Country', 'easy-digital-downloads' ),
|
||||
'options' => $countries,
|
||||
'selected' => $country,
|
||||
'class' => 'card-update-field address_country',
|
||||
'data' => array( 'key' => 'address_country' ),
|
||||
'show_option_all' => false,
|
||||
'show_option_none' => false,
|
||||
) );
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p class="edds-card-address-field edds-card-address-field--state">
|
||||
<label for="<?php echo esc_attr( sprintf( 'edds_address_state_%1$s', $source->id ) ); ?>">
|
||||
<?php esc_html_e( 'State', 'easy-digital-downloads' ); ?>
|
||||
</label>
|
||||
|
||||
<?php
|
||||
$selected_state = isset( $source->address_state ) ? $source->address_state : edd_get_shop_state();
|
||||
$states = edd_get_shop_states( $country );
|
||||
echo EDD()->html->select( array(
|
||||
'id' => sprintf( 'edds_address_state_%1$s', $source->id ),
|
||||
'name' => 'address_state',
|
||||
'options' => $states,
|
||||
'selected' => $selected_state,
|
||||
'class' => 'card-update-field address_state card_state',
|
||||
'data' => array( 'key' => 'address_state' ),
|
||||
'show_option_all' => false,
|
||||
'show_option_none' => false,
|
||||
) );
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="card-expiration-fields">
|
||||
<label for="<?php echo esc_attr( sprintf( 'edds_card_exp_month_%1$s', $source->id ) ); ?>" class="edd-label">
|
||||
<?php _e( 'Expiration (MM/YY)', 'easy-digital-downloads' ); ?>
|
||||
</label>
|
||||
|
||||
<?php
|
||||
$months = array_combine( $r = range( 1, 12 ), $r );
|
||||
echo EDD()->html->select( array(
|
||||
'id' => sprintf( 'edds_card_exp_month_%1$s', $source->id ),
|
||||
'name' => 'exp_month',
|
||||
'options' => $months,
|
||||
'selected' => $source->exp_month,
|
||||
'class' => 'card-expiry-month edd-select edd-select-small card-update-field exp_month',
|
||||
'data' => array( 'key' => 'exp_month' ),
|
||||
'show_option_all' => false,
|
||||
'show_option_none' => false,
|
||||
) );
|
||||
?>
|
||||
|
||||
<span class="exp-divider"> / </span>
|
||||
|
||||
<?php
|
||||
$years = array_combine( $r = range( date( 'Y' ), date( 'Y' ) + 30 ), $r );
|
||||
echo EDD()->html->select( array(
|
||||
'id' => sprintf( 'edds_card_exp_year_%1$s', $source->id ),
|
||||
'name' => 'exp_year',
|
||||
'options' => $years,
|
||||
'selected' => $source->exp_year,
|
||||
'class' => 'card-expiry-year edd-select edd-select-small card-update-field exp_year',
|
||||
'data' => array( 'key' => 'exp_year' ),
|
||||
'show_option_all' => false,
|
||||
'show_option_none' => false,
|
||||
) );
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input
|
||||
type="submit"
|
||||
class="edd-stripe-submit-update"
|
||||
data-loading="<?php echo esc_attr__( 'Please Wait…', 'easy-digital-downloads' ); ?>"
|
||||
data-submit="<?php echo esc_attr__( 'Update Card', 'easy-digital-downloads' ); ?>"
|
||||
value="<?php echo esc_attr__( 'Update Card', 'easy-digital-downloads' ); ?>"
|
||||
/>
|
||||
|
||||
<a href="#" class="edd-stripe-cancel-update" data-source="<?php echo esc_attr( $source->id ); ?>"><?php _e( 'Cancel', 'easy-digital-downloads' ); ?></a>
|
||||
|
||||
<input type="hidden" name="card_id" data-key="id" value="<?php echo esc_attr( $source->id ); ?>" />
|
||||
<?php
|
||||
wp_nonce_field( $source->id . '_update', 'card_update_nonce_' . $source->id, true );
|
||||
echo edds_get_tokenizer_input( $source->id );
|
||||
?>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
<form id="edd-stripe-add-new-card">
|
||||
<div class="edd-stripe-add-new-card edd-stripe-new-card" style="display: none;">
|
||||
<label><?php _e( 'Add New Card', 'easy-digital-downloads' ); ?></label>
|
||||
<fieldset id="edd_cc_card_info" class="cc-card-info">
|
||||
<legend><?php _e( 'Credit Card Details', 'easy-digital-downloads' ); ?></legend>
|
||||
<?php do_action( 'edd_stripe_new_card_form' ); ?>
|
||||
</fieldset>
|
||||
<?php
|
||||
switch( $display ) {
|
||||
case 'full' :
|
||||
edd_default_cc_address_fields();
|
||||
break;
|
||||
|
||||
case 'zip_country' :
|
||||
edd_stripe_zip_and_country();
|
||||
add_filter( 'edd_purchase_form_required_fields', 'edd_stripe_require_zip_and_country' );
|
||||
|
||||
break;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="edd-stripe-add-card-errors"></div>
|
||||
<div class="edd-stripe-add-card-actions">
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
class="edd-button edd-stripe-add-new"
|
||||
data-loading="<?php echo esc_attr__( 'Please Wait…', 'easy-digital-downloads' ); ?>"
|
||||
data-submit="<?php echo esc_attr__( 'Add new card', 'easy-digital-downloads' ); ?>"
|
||||
value="<?php echo esc_attr__( 'Add new card', 'easy-digital-downloads' ); ?>"
|
||||
/>
|
||||
<a href="#" id="edd-stripe-add-new-cancel" style="display: none;"><?php _e( 'Cancel', 'easy-digital-downloads' ); ?></a>
|
||||
<?php
|
||||
wp_nonce_field( 'edd-stripe-add-card', 'edd-stripe-add-card-nonce', false, true );
|
||||
echo edds_get_tokenizer_input();
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
add_action( 'edd_profile_editor_after', 'edd_stripe_manage_cards' );
|
||||
|
||||
/**
|
||||
* Determines if the default Profile Editor's "Billing Address"
|
||||
* fields should be hidden.
|
||||
*
|
||||
* If using Stripe + Saved Cards (and Stripe is the only active gateway)
|
||||
* the information set in "Billing Address" is never utilized:
|
||||
*
|
||||
* - When using an existing Card that Card's billing address is used.
|
||||
* - When adding a new Card the address form is blanked.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
function edd_stripe_maybe_hide_profile_editor_billing_address() {
|
||||
if ( false === edds_is_gateway_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only hide if Stripe is the only active gateway.
|
||||
$active_gateways = edd_get_enabled_payment_gateways();
|
||||
|
||||
if ( ! ( 1 === count( $active_gateways ) && isset( $active_gateways['stripe'] ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only hide if using Saved Cards.
|
||||
$use_saved_cards = edd_stripe_existing_cards_enabled();
|
||||
|
||||
if ( false === $use_saved_cards ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow a default addres to be entered for the first Card
|
||||
// if the Profile Editor is found before Checkout.
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
|
||||
if ( empty( $existing_cards ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<style>#edd_profile_address_fieldset { display: none; }</style>';
|
||||
}
|
||||
add_action( 'edd_profile_editor_after', 'edd_stripe_maybe_hide_profile_editor_billing_address' );
|
||||
|
||||
/**
|
||||
* Zip / Postal Code field for when full billing address is disabled
|
||||
*
|
||||
* @since 2.5
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_zip_and_country() {
|
||||
|
||||
$logged_in = is_user_logged_in();
|
||||
$customer = EDD()->session->get( 'customer' );
|
||||
$customer = wp_parse_args( $customer, array( 'address' => array(
|
||||
'line1' => '',
|
||||
'line2' => '',
|
||||
'city' => '',
|
||||
'zip' => '',
|
||||
'state' => '',
|
||||
'country' => ''
|
||||
) ) );
|
||||
|
||||
$customer['address'] = array_map( 'sanitize_text_field', $customer['address'] );
|
||||
|
||||
if( $logged_in ) {
|
||||
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
|
||||
if ( empty( $existing_cards ) ) {
|
||||
|
||||
$user_address = edd_get_customer_address( get_current_user_id() );
|
||||
|
||||
foreach( $customer['address'] as $key => $field ) {
|
||||
|
||||
if ( empty( $field ) && ! empty( $user_address[ $key ] ) ) {
|
||||
$customer['address'][ $key ] = $user_address[ $key ];
|
||||
} else {
|
||||
$customer['address'][ $key ] = '';
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
foreach ( $existing_cards as $card ) {
|
||||
if ( false === $card['default'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source = $card['source'];
|
||||
$customer['address'] = array(
|
||||
'line1' => $source->address_line1,
|
||||
'line2' => $source->address_line2,
|
||||
'city' => $source->address_city,
|
||||
'zip' => $source->address_zip,
|
||||
'state' => $source->address_state,
|
||||
'country' => $source->address_country,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
<fieldset id="edd_cc_address" class="cc-address">
|
||||
<legend><?php _e( 'Billing Details', 'easy-digital-downloads' ); ?></legend>
|
||||
<p id="edd-card-country-wrap">
|
||||
<label for="billing_country" class="edd-label">
|
||||
<?php _e( 'Billing Country', 'easy-digital-downloads' ); ?>
|
||||
<?php if( edd_field_is_required( 'billing_country' ) ) { ?>
|
||||
<span class="edd-required-indicator">*</span>
|
||||
<?php } ?>
|
||||
</label>
|
||||
<span class="edd-description"><?php _e( 'The country for your billing address.', 'easy-digital-downloads' ); ?></span>
|
||||
<select name="billing_country" id="billing_country" class="billing_country edd-select<?php if( edd_field_is_required( 'billing_country' ) ) { echo ' required'; } ?>"<?php if( edd_field_is_required( 'billing_country' ) ) { echo ' required '; } ?> autocomplete="billing country">
|
||||
<?php
|
||||
|
||||
$selected_country = edd_get_shop_country();
|
||||
|
||||
if( ! empty( $customer['address']['country'] ) && '*' !== $customer['address']['country'] ) {
|
||||
$selected_country = $customer['address']['country'];
|
||||
}
|
||||
|
||||
$countries = edd_get_country_list();
|
||||
foreach( $countries as $country_code => $country ) {
|
||||
echo '<option value="' . esc_attr( $country_code ) . '"' . selected( $country_code, $selected_country, false ) . '>' . esc_html( $country ) . '</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</p>
|
||||
<p id="edd-card-zip-wrap">
|
||||
<label for="card_zip" class="edd-label">
|
||||
<?php _e( 'Billing Zip / Postal Code', 'easy-digital-downloads' ); ?>
|
||||
<?php if( edd_field_is_required( 'card_zip' ) ) { ?>
|
||||
<span class="edd-required-indicator">*</span>
|
||||
<?php } ?>
|
||||
</label>
|
||||
<span class="edd-description"><?php _e( 'The zip or postal code for your billing address.', 'easy-digital-downloads' ); ?></span>
|
||||
<input type="text" size="4" name="card_zip" id="card_zip" class="card-zip edd-input<?php if( edd_field_is_required( 'card_zip' ) ) { echo ' required'; } ?>" placeholder="<?php _e( 'Zip / Postal Code', 'easy-digital-downloads' ); ?>" value="<?php echo esc_attr( $customer['address']['zip'] ); ?>"<?php if( edd_field_is_required( 'card_zip' ) ) { echo ' required '; } ?> autocomplete="billing postal-code" />
|
||||
</p>
|
||||
</fieldset>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine how the billing address fields should be displayed
|
||||
*
|
||||
* @access public
|
||||
* @since 2.5
|
||||
* @return void
|
||||
*/
|
||||
function edd_stripe_setup_billing_address_fields() {
|
||||
|
||||
if( ! function_exists( 'edd_use_taxes' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( edd_use_taxes() || 'stripe' !== edd_get_chosen_gateway() || ! edd_get_cart_total() > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$display = edd_get_option( 'stripe_billing_fields', 'full' );
|
||||
|
||||
switch( $display ) {
|
||||
|
||||
case 'full' :
|
||||
|
||||
// Make address fields required
|
||||
add_filter( 'edd_require_billing_address', '__return_true' );
|
||||
|
||||
break;
|
||||
|
||||
case 'zip_country' :
|
||||
|
||||
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields', 10 );
|
||||
add_action( 'edd_after_cc_fields', 'edd_stripe_zip_and_country', 9 );
|
||||
|
||||
// Make Zip required
|
||||
add_filter( 'edd_purchase_form_required_fields', 'edd_stripe_require_zip_and_country' );
|
||||
|
||||
break;
|
||||
|
||||
case 'none' :
|
||||
|
||||
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields', 10 );
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
add_action( 'init', 'edd_stripe_setup_billing_address_fields', 9 );
|
||||
|
||||
/**
|
||||
* Force zip code and country to be required when billing address display is zip only
|
||||
*
|
||||
* @access public
|
||||
* @since 2.5
|
||||
* @return array $fields The required fields
|
||||
*/
|
||||
function edd_stripe_require_zip_and_country( $fields ) {
|
||||
|
||||
$fields['card_zip'] = array(
|
||||
'error_id' => 'invalid_zip_code',
|
||||
'error_message' => __( 'Please enter your zip / postal code', 'easy-digital-downloads' )
|
||||
);
|
||||
|
||||
$fields['billing_country'] = array(
|
||||
'error_id' => 'invalid_country',
|
||||
'error_message' => __( 'Please select your billing country', 'easy-digital-downloads' )
|
||||
);
|
||||
|
||||
return $fields;
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* Generic registry functionality.
|
||||
*
|
||||
* @todo Replace with EDD\Utils\Registry when EDD 3.x is required.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6.19
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the construct for building an item registry.
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @abstract
|
||||
*/
|
||||
abstract class EDD_Stripe_Utils_Registry extends ArrayObject {
|
||||
|
||||
/**
|
||||
* Item error label.
|
||||
*
|
||||
* Used for customizing exception messages to the current registry instance. Default 'item'.
|
||||
*
|
||||
* @since 2.6.19
|
||||
* @var string
|
||||
*/
|
||||
public static $item_error_label = 'item';
|
||||
|
||||
/**
|
||||
* Adds an item to the registry.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @throws Exception If the `$attributes` array is empty.
|
||||
*
|
||||
* @param string $item_id Item ID.
|
||||
* @param array $attributes Array of item attributes. Each extending registry will
|
||||
* handle item ID and attribute building in different ways.
|
||||
* @return bool True if `$attributes` is not empty, otherwise false.
|
||||
*/
|
||||
public function add_item( $item_id, $attributes ) {
|
||||
$result = false;
|
||||
|
||||
if ( ! empty( $attributes ) ) {
|
||||
$this->offsetSet( $item_id, $attributes );
|
||||
|
||||
$result = true;
|
||||
} else {
|
||||
$message = sprintf(
|
||||
'The attributes were missing when attempting to add the \'%1$s\' %2$s.',
|
||||
$item_id,
|
||||
static::$item_error_label
|
||||
);
|
||||
|
||||
throw new Exception( $message );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the registry by ID.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $item_id Item ID.
|
||||
*/
|
||||
public function remove_item( $item_id ) {
|
||||
if ( $this->offsetExists( $item_id ) ) {
|
||||
$this->offsetUnset( $item_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item and its associated attributes.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @throws Exception if the item does not exist.
|
||||
*
|
||||
* @param string $item_id Item ID.
|
||||
* @return array Array of attributes for the item if the item is set,
|
||||
* otherwise an empty array.
|
||||
*/
|
||||
public function get_item( $item_id ) {
|
||||
$item = array();
|
||||
|
||||
if ( $this->offsetExists( $item_id ) ) {
|
||||
$item = $this->offsetGet( $item_id );
|
||||
} else {
|
||||
$message = sprintf(
|
||||
'The \'%1$s\' %2$s does not exist.',
|
||||
$item_id,
|
||||
static::$item_error_label
|
||||
);
|
||||
|
||||
throw new Exception( $message );
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves registered items.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return array The list of registered items.
|
||||
*/
|
||||
public function get_items() {
|
||||
return $this->getArrayCopy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a given attribute for a given item.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @throws Exception if the item does not exist.
|
||||
* @throws EDD_Stripe_Exceptions_Attribute_Not_Found if the attribute and/or item does not exist.
|
||||
*
|
||||
* @param string $key Key of the attribute to retrieve.
|
||||
* @param string $item_id Collection to retrieve the attribute from.
|
||||
* @return mixed|null The attribute value if set, otherwise null.
|
||||
*/
|
||||
public function get_attribute( $key, $item_id ) {
|
||||
$attribute = null;
|
||||
$item = $this->get_item( $item_id );
|
||||
|
||||
if ( ! empty( $item[ $key ] ) ) {
|
||||
$attribute = $item[ $key ];
|
||||
} else {
|
||||
throw EDD_Stripe_Utils_Exceptions_Attribute_Not_Found::from_attr( $key, $item_id );
|
||||
}
|
||||
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* EDD_Stripe_Utils_Exceptions_Attribute_Not_Found exception class
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6.19
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements an EDD_Stripe_Utils_Exceptions_Attribute_Not_Found exception thrown when a given
|
||||
* attribute is not found.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @see OutOfBoundsException
|
||||
*/
|
||||
class EDD_Stripe_Utils_Exceptions_Attribute_Not_Found extends OutOfBoundsException {
|
||||
|
||||
/**
|
||||
* Retrieves an informed Attribute_Not_Found instance via late-static binding.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @param string $attribute_name Attribute resulting in the exception.
|
||||
* @param string $collection Collection the attribute belongs to.
|
||||
* @param int $code Optional. Exception code. Default null.
|
||||
* @param Exception $previous Optional. Previous exception (used for chaining).
|
||||
* Default null.
|
||||
* @return EDD_Stripe_Exceptions_Attribute_Not_Found Exception instance.
|
||||
*/
|
||||
public static function from_attr( $attribute_name, $collection, $code = null, $previous = null ) {
|
||||
$message = sprintf( 'The \'%1$s\' attribute does not exist for \'%2$s\'.',
|
||||
$attribute_name,
|
||||
$collection
|
||||
);
|
||||
|
||||
return new static( $message, $code, $previous);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Exception: Gateway
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @copyright Copyright (c) 2021, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.1
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* EDD_Stripe_Gateway_Exception class.
|
||||
*/
|
||||
class EDD_Stripe_Gateway_Exception extends \Exception {
|
||||
|
||||
/**
|
||||
* Log message, not displayed to the user.
|
||||
*
|
||||
* @since 2.8.1
|
||||
* @var string
|
||||
*/
|
||||
public $log_message;
|
||||
|
||||
/**
|
||||
* Constructs the exception.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @param string $display_message User-facing exception message.
|
||||
* @param string $log_message Optional. Admin-facing exception message.
|
||||
* @param int $http_status_code Optional. HTTP status code to respond with, e.g. 400.
|
||||
*/
|
||||
public function __construct(
|
||||
$display_message,
|
||||
$log_message = '',
|
||||
$http_status_code = 400
|
||||
) {
|
||||
parent::__construct( $display_message, $http_status_code );
|
||||
|
||||
if ( ! empty( $log_message ) ) {
|
||||
$this->log_message = $log_message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an admin-facing message is set.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLogMessage() {
|
||||
return ! empty( $this->log_message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the admin-facing log message.
|
||||
*
|
||||
* @since 2.8.1
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLogMessage() {
|
||||
if ( false === $this->hasLogMessage() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->log_message;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* EDD_Stripe_Utils_Exceptions_Stripe_API_Unmet_Requirements exception class
|
||||
*
|
||||
* @package EDD_Stripe\Utils\Exceptions
|
||||
* @copyright Copyright (c) 2021, Sandhills Development, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 2.8.1
|
||||
*/
|
||||
|
||||
/**
|
||||
* EDD_Stripe_Utils_Exceptions_Stripe_API_Unmet_Requirements
|
||||
*
|
||||
* @since 2.8.1
|
||||
*/
|
||||
class EDD_Stripe_Utils_Exceptions_Stripe_API_Unmet_Requirements extends \Exception {
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* EDD_Stripe_Utils_Exceptions_Stripe_Object_Not_Found exception class
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6.19
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements an EDD_Stripe_Utils_Exceptions_Stripe_Object_Not_Found exception.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
class EDD_Stripe_Utils_Exceptions_Stripe_Object_Not_Found extends BadMethodCallException {
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* EDD_Stripe_Utils_Static_Registry interface.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.6.19
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the contract for a static (singleton) registry object.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*/
|
||||
interface EDD_Stripe_Utils_Static_Registry {
|
||||
|
||||
/**
|
||||
* Retrieves the one true registry instance.
|
||||
*
|
||||
* @since 2.6.19
|
||||
*
|
||||
* @return EDD_Stripe_Utils_Static_Registry Registry instance.
|
||||
*/
|
||||
public static function instance();
|
||||
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* Utils: Modal
|
||||
*
|
||||
* Generic "Modal" helper for generating markup.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generates HTML for a Modal.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param int $download_id
|
||||
* @param array $args
|
||||
*/
|
||||
function edds_modal( $args = array() ) {
|
||||
$defaults = array(
|
||||
'id' => '',
|
||||
'class' => array(),
|
||||
'title' => '',
|
||||
'content' => ''
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
// ID is required for any Javascript interaction
|
||||
// and accessibility.
|
||||
if ( empty( $args['id'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classnames = implode( ' ', array_map( 'trim', (array) $args['class'] ) );
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div
|
||||
id="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
class="edds-modal has-slide"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div
|
||||
class="edds-modal__overlay"
|
||||
tabindex="-1"
|
||||
data-micromodal-close
|
||||
>
|
||||
<div
|
||||
class="edds-modal__container <?php echo esc_attr( $classnames ); ?>"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="<?php echo esc_attr( $args['id'] ); ?>-modal-title"
|
||||
>
|
||||
<header class="edds-modal__header">
|
||||
<?php if ( ! empty( $args['title'] ) ) : ?>
|
||||
<h2
|
||||
id="<?php echo esc_attr( $args['id'] ); ?>-modal-title"
|
||||
class="edds-modal__title"
|
||||
>
|
||||
<?php echo esc_html( $args['title'] ); ?>
|
||||
</h2>
|
||||
<?php endif; ?>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="edds-modal__close"
|
||||
aria-label="<?php echo esc_attr_e( 'Close', 'easy-digital-downloads' ); ?>"
|
||||
data-micromodal-close
|
||||
>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div
|
||||
id="<?php echo esc_attr( $args['id'] ); ?>-modal-content"
|
||||
class="edds-modal__content"
|
||||
>
|
||||
<?php echo $args['content']; // WPCS: XSS okay. ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
/**
|
||||
* Webhooks.
|
||||
*
|
||||
* @package EDD_Stripe
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Listen for Stripe Webhooks.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
function edds_stripe_event_listener() {
|
||||
if ( ! isset( $_GET['edd-listener'] ) || 'stripe' !== $_GET['edd-listener'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Retrieve the request's body and parse it as JSON.
|
||||
$body = @file_get_contents( 'php://input' );
|
||||
$event = json_decode( $body );
|
||||
|
||||
if ( isset( $event->id ) ) {
|
||||
$event = edds_api_request( 'Event', 'retrieve', $event->id );
|
||||
} else {
|
||||
throw new \Exception( esc_html__( 'Unable to find Event', 'easy-digital-downloads' ) );
|
||||
}
|
||||
|
||||
// Handle events.
|
||||
//
|
||||
switch ( $event->type ) {
|
||||
|
||||
// Charge succeeded. Update EDD Payment address.
|
||||
case 'charge.succeeded' :
|
||||
$charge = $event->data->object;
|
||||
$payment_id = edd_get_purchase_id_by_transaction_id( $charge->id );
|
||||
$payment = new EDD_Payment( $payment_id );
|
||||
|
||||
if ( $payment && $payment->ID > 0 ) {
|
||||
$payment->address = array(
|
||||
'line1' => $charge->billing_details->address->line1,
|
||||
'line2' => $charge->billing_details->address->line2,
|
||||
'state' => $charge->billing_details->address->state,
|
||||
'city' => $charge->billing_details->address->city,
|
||||
'zip' => $charge->billing_details->address->postal_code,
|
||||
'country' => $charge->billing_details->address->country,
|
||||
);
|
||||
|
||||
$payment->save();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Charge refunded. Ensure EDD Payment status is correct.
|
||||
case 'charge.refunded':
|
||||
$charge = $event->data->object;
|
||||
$payment_id = edd_get_purchase_id_by_transaction_id( $charge->id );
|
||||
$payment = new EDD_Payment( $payment_id );
|
||||
|
||||
// This is an uncaptured PaymentIntent, not a true refund.
|
||||
if ( ! $charge->captured ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $payment && $payment->ID > 0 ) {
|
||||
|
||||
// If this was completely refunded, set the status to refunded.
|
||||
if ( $charge->refunded ) {
|
||||
$payment->status = 'refunded';
|
||||
$payment->save();
|
||||
// Translators: The charge ID from Stripe that is being refunded.
|
||||
$payment->add_note( sprintf( __( 'Charge %s has been fully refunded in Stripe.', 'easy-digital-downloads' ), $charge->id ) );
|
||||
|
||||
// If this was partially refunded, don't change the status.
|
||||
} else {
|
||||
// Translators: The charge ID from Stripe that is being partially refunded.
|
||||
$payment->add_note( sprintf( __( 'Charge %s partially refunded in Stripe.', 'easy-digital-downloads' ), $charge->id ) );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Review started.
|
||||
case 'review.opened' :
|
||||
$is_live = ! edd_is_test_mode();
|
||||
$review = $event->data->object;
|
||||
|
||||
// Make sure the modes match.
|
||||
if ( $is_live !== $review->livemode ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$charge = $review->charge;
|
||||
|
||||
// Get the charge from the PaymentIntent.
|
||||
if ( ! $charge ) {
|
||||
$payment_intent = $review->payment_intent;
|
||||
|
||||
if ( ! $payment_intent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment_intent = edds_api_request( 'PaymentIntent', 'retrieve', $payment_intent );
|
||||
$charge = $payment_intent->charges->data[0]->id;
|
||||
}
|
||||
|
||||
$payment_id = edd_get_purchase_id_by_transaction_id( $charge );
|
||||
$payment = new EDD_Payment( $payment_id );
|
||||
|
||||
if ( $payment && $payment->ID > 0 ) {
|
||||
$payment->add_note(
|
||||
sprintf(
|
||||
/* translators: %s Stripe Radar review opening reason. */
|
||||
__( 'Stripe Radar review opened with a reason of %s.', 'easy-digital-downloads' ),
|
||||
$review->reason
|
||||
)
|
||||
);
|
||||
$payment->save();
|
||||
|
||||
do_action( 'edd_stripe_review_opened', $review, $payment_id );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Review closed.
|
||||
case 'review.closed' :
|
||||
$is_live = ! edd_is_test_mode();
|
||||
$review = $event->data->object;
|
||||
|
||||
// Make sure the modes match
|
||||
if ( $is_live !== $review->livemode ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$charge = $review->charge;
|
||||
|
||||
// Get the charge from the PaymentIntent.
|
||||
if ( ! $charge ) {
|
||||
$payment_intent = $review->payment_intent;
|
||||
|
||||
if ( ! $payment_intent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment_intent = edds_api_request( 'PaymentIntent', 'retrieve', $payment_intent );
|
||||
$charge = $payment_intent->charges->data[0]->id;
|
||||
}
|
||||
|
||||
$payment_id = edd_get_purchase_id_by_transaction_id( $charge );
|
||||
$payment = new EDD_Payment( $payment_id );
|
||||
|
||||
if ( $payment && $payment->ID > 0 ) {
|
||||
$payment->add_note(
|
||||
sprintf(
|
||||
/* translators: %s Stripe Radar review closing reason. */
|
||||
__( 'Stripe Radar review closed with a reason of %s.', 'easy-digital-downloads' ),
|
||||
$review->reason
|
||||
)
|
||||
);
|
||||
$payment->save();
|
||||
|
||||
do_action( 'edd_stripe_review_closed', $review, $payment_id );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
do_action( 'edds_stripe_event_' . $event->type, $event );
|
||||
|
||||
// Nothing failed, mark complete.
|
||||
status_header( 200 );
|
||||
die( esc_html( 'EDD Stripe: ' . $event->type ) );
|
||||
|
||||
// Fail, allow a retry.
|
||||
} catch ( \Exception $e ) {
|
||||
status_header( 500 );
|
||||
die( '-2' );
|
||||
}
|
||||
}
|
||||
add_action( 'init', 'edds_stripe_event_listener' );
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user