updated plugin Easy Digital Downloads version 3.1.1.2

This commit is contained in:
2023-03-17 22:34:04 +00:00
committed by Gitium
parent e8a66564bd
commit 19e086d1c4
647 changed files with 20986 additions and 27305 deletions

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

View File

@ -0,0 +1,3 @@
#edd-stripe-card-errors:not(:empty){margin:20px 0 0}: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:rgba(0, 0, 0, 0.60);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(1rem*1.5);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}.edds-modal__close:before{content:"✕"}.edds-modal__content{margin:calc(1rem*1.5);margin:calc(var(--edds-modal-grid-unit)*1.5)}@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 .3s cubic-bezier(0, 0, 0.2, 1)}.edds-modal.has-slide[aria-hidden=true] .edds-modal__container{animation:eddsSlideOut .3s cubic-bezier(0, 0, 0.2, 1)}.edds-modal.has-slide .edds-modal__container,.edds-modal.has-slide .edds-modal__overlay{will-change:transform}.edds-buy-now-modal{width:500px}.edds-buy-now-modal .edds-modal__close{padding:.5rem}.edds-buy-now-modal #edd_checkout_form_wrap input.edd-input,.edds-buy-now-modal #edd_checkout_form_wrap textarea.edd-input{width:100%}.edds-buy-now-modal #edd_checkout_form_wrap #edd_purchase_submit{margin-top:1.5rem;margin-bottom:0}.edds-buy-now-modal .edds-field-spacer-shim{margin-bottom:1rem}.edds-buy-now-modal .edd-alert-error{margin:20px 0}.edds-buy-now-modal #edd-stripe-card-errors:not(:empty){margin-bottom:20px}.edds-buy-now-modal #edd-stripe-card-errors:not(:empty) .edd-alert-error{margin:0}#edd_purchase_submit #edd-purchase-button[data-edd-button-state=disabled]{opacity:.5;cursor:not-allowed}#edd_purchase_submit #edd-purchase-button[data-edd-button-state=updating],#edd_purchase_submit #edd-purchase-button [data-edd-button-state=processing]{opacity:.5;cursor:wait}
/*# sourceMappingURL=paymentelements.min.css.map*/

View File

@ -0,0 +1 @@
{"version":3,"sources":["webpack:///./assets/css/src/payment-elements.scss"],"names":[],"mappings":"AAAA,oCAAoC,gBAAgB,MAAM,6BAA6B,0CAA0C,qBAAqB,aAAa,eAAe,MAAM,OAAO,QAAQ,SAAS,+BAA+B,qCAAqC,aAAa,uBAAuB,mBAAmB,uBAAuB,sBAAsB,gBAAgB,eAAe,gBAAgB,sBAAsB,gBAAgB,kCAAkC,gBAAgB,oBAAoB,uBAAuB,8CAA8C,aAAa,8BAA8B,mBAAmB,gBAAgB,MAAM,UAAU,gBAAgB,6BAA6B,mBAAmB,gBAAgB,eAAe,SAAS,mBAAmB,cAAc,aAAa,0BAA0B,YAAY,qBAAqB,sBAAsB,6CAA6C,uBAAuB,KAAK,0BAA0B,GAAG,yBAAyB,wBAAwB,KAAK,wBAAwB,GAAG,2BAA2B,sBAAsB,aAAa,8BAA8B,cAAc,gEAAgE,qDAAqD,+DAA+D,sDAAsD,wFAAwF,sBAAsB,oBAAoB,YAAY,uCAAuC,cAAc,2HAA2H,WAAW,iEAAiE,kBAAkB,gBAAgB,4CAA4C,mBAAmB,qCAAqC,cAAc,wDAAwD,mBAAmB,yEAAyE,SAAS,0EAA0E,WAAW,mBAAmB,uJAAuJ,WAAW,Y","file":"assets/css/build/paymentelements.min.css","sourcesContent":["#edd-stripe-card-errors:not(:empty){margin:20px 0 0}: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:rgba(0, 0, 0, 0.60);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(1rem*1.5);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}.edds-modal__close:before{content:\"✕\"}.edds-modal__content{margin:calc(1rem*1.5);margin:calc(var(--edds-modal-grid-unit)*1.5)}@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 .3s cubic-bezier(0, 0, 0.2, 1)}.edds-modal.has-slide[aria-hidden=true] .edds-modal__container{animation:eddsSlideOut .3s cubic-bezier(0, 0, 0.2, 1)}.edds-modal.has-slide .edds-modal__container,.edds-modal.has-slide .edds-modal__overlay{will-change:transform}.edds-buy-now-modal{width:500px}.edds-buy-now-modal .edds-modal__close{padding:.5rem}.edds-buy-now-modal #edd_checkout_form_wrap input.edd-input,.edds-buy-now-modal #edd_checkout_form_wrap textarea.edd-input{width:100%}.edds-buy-now-modal #edd_checkout_form_wrap #edd_purchase_submit{margin-top:1.5rem;margin-bottom:0}.edds-buy-now-modal .edds-field-spacer-shim{margin-bottom:1rem}.edds-buy-now-modal .edd-alert-error{margin:20px 0}.edds-buy-now-modal #edd-stripe-card-errors:not(:empty){margin-bottom:20px}.edds-buy-now-modal #edd-stripe-card-errors:not(:empty) .edd-alert-error{margin:0}#edd_purchase_submit #edd-purchase-button[data-edd-button-state=disabled]{opacity:.5;cursor:not-allowed}#edd_purchase_submit #edd-purchase-button[data-edd-button-state=updating],#edd_purchase_submit #edd-purchase-button [data-edd-button-state=processing]{opacity:.5;cursor:wait}"],"sourceRoot":""}

View File

@ -1,399 +0,0 @@
/**
* 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;
}
}
}

View File

@ -1,103 +0,0 @@
: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;
}

View File

@ -1,83 +0,0 @@
.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

View File

@ -1 +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}});
!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=109)}({109: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(6))},6:function(e,n){e.exports=jQuery}});

File diff suppressed because one or more lines are too long

View File

@ -1,104 +0,0 @@
/* 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';
},
};

View File

@ -1,36 +0,0 @@
/* 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,
}
);
} );
} );
} );

View File

@ -1,5 +0,0 @@
/**
* Internal dependencies
*/
import './requirements.js';
import './stripe-connect.js';

View File

@ -1,40 +0,0 @@
/**
* 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' ) );
} );

View File

@ -1,38 +0,0 @@
/**
* 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;
}
/*
* Do not make a request, if we are inside Onboarding Wizard.
* Onboarding Wizard will make it's own call.
*/
if ( containerEl.hasAttribute('data-onboarding-wizard') ) {
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' );
} );
} );

View File

@ -1,2 +0,0 @@
export { default as Modal } from './modal';
export { paymentMethods } from './payment-methods';

View File

@ -1,46 +0,0 @@
/**
* 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,
};

View File

@ -1,259 +0,0 @@
/* 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;
}
}
}

View File

@ -1,64 +0,0 @@
/* 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 );
}
} )();

View File

@ -1,205 +0,0 @@
/* 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 );
} );
}

View File

@ -1,36 +0,0 @@
/* 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();
} );
}

View File

@ -1,272 +0,0 @@
/* 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 );
}
}

View File

@ -1,8 +0,0 @@
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';

View File

@ -1,17 +0,0 @@
/**
* 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();
}

View File

@ -1,133 +0,0 @@
/* 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 );
}

View File

@ -1,458 +0,0 @@
/* 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 );
} );
}

View File

@ -1,416 +0,0 @@
/* 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 );
}

View File

@ -1,27 +0,0 @@
/**
* 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;
}

View File

@ -1,14 +0,0 @@
/**
* 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();
}

View File

@ -1,197 +0,0 @@
/* 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' ) );
}

View File

@ -1,188 +0,0 @@
/* 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' ) );
}

View File

@ -1,312 +0,0 @@
/* 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' ) ),
},
};
}

View File

@ -1,179 +0,0 @@
/* 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;
}

View File

@ -1,51 +0,0 @@
/* 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;
}

View File

@ -1,43 +0,0 @@
/**
* 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;
}

View File

@ -1,58 +0,0 @@
/**
* 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;
}

View File

@ -1,9 +0,0 @@
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';

View File

@ -1,61 +0,0 @@
/* 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( '' );
}

View File

@ -1,21 +0,0 @@
/// 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;
};
}

View File

@ -1,17 +0,0 @@
// 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;
}
};
}

View File

@ -1,15 +0,0 @@
/// 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;
};
}

View File

@ -1,18 +0,0 @@
/// 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 ] );

View File

@ -3,9 +3,9 @@
* 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.1
* Requires at least: 4.9
* Requires PHP: 5.6
* Version: 2.9.2
* Requires at least: 5.4
* Requires PHP: 7.1
* Author: Easy Digital Downloads
* Author URI: https://easydigitaldownloads.com
* Text Domain: edds
@ -44,7 +44,7 @@ function edd_stripe_core_bootstrap() {
}
if ( ! defined( 'EDD_STRIPE_VERSION' ) ) {
define( 'EDD_STRIPE_VERSION', '2.8.13.1' );
define( 'EDD_STRIPE_VERSION', '2.9.2' );
}
if ( ! defined( 'EDD_STRIPE_API_VERSION' ) ) {

View File

@ -61,11 +61,7 @@ function edds_payments_column_data( $value, $payment_id, $column_name ) {
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 );
}
$customer_id = edd_get_order_meta( $payment_id, '_edds_stripe_customer_id', true );
if ( empty( $customer_id ) ) {
return $value;

View File

@ -23,7 +23,7 @@ if ( ! defined( 'ABSPATH' ) ) {
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' ),
__( 'To continue accepting credit card payments with Stripe please update %1$sEasy Digital Downloads%2$s to version %3$s3.1%4$s or higher.', 'easy-digital-downloads' ),
'<strong>',
'</strong>',
'<code>',

View File

@ -1,26 +0,0 @@
<?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'
);
}
}

View File

@ -1,10 +1,12 @@
<?php
/**
* Register our settings section
*
* @return array
*/
* Register our settings section
*
* @param array $sections The Registered EDD Sections array.
*
* @return array
*/
function edds_settings_section( $sections ) {
$sections['edd-stripe'] = __( 'Stripe', 'easy-digital-downloads' );
@ -17,185 +19,200 @@ add_filter( 'edd_settings_sections_gateways', 'edds_settings_section' );
*
* @access public
* @since 1.0
*
* @param array $settings The currently registered settings.
*
* @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',
'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',
'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',
'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',
'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',
'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' =>
'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(
) .
'</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(
) .
'</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 href="' . esc_url( edds_documentation_route( 'stripe' ) ) . '#webhook-configuration' . '" target="_blank" rel="noopener noreferrer">',
'</a>'
) . '</p>'
) .
'</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',
'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',
'std' => 'full',
'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' )
'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_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_allow_prepaid' => array(
'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 ( _edds_legacy_elements_enabled() ) {
$elements_mode = edds_get_elements_mode();
if ( ! edds_stripe_connect_can_manage_keys() ) {
$stripe_settings['stripe_elements_mode'] = array(
'id' => 'stripe_elements_mode',
'name' => __( 'Elements Mode', 'easy-digital-downloads' ),
'desc' => __( 'Toggle between using the legacy Card Elements Stripe integration and the new Payment Elements experience.', 'easy-digital-downloads' ),
'type' => 'select',
'options' => array(
'card-elements' => __( 'Card Element', 'easy-digital-downloads' ),
'payment-elements' => __( 'Payment Element', 'easy-digital-downloads' ),
),
'class' => 'stripe-elements-mode',
'tooltip_title' => __( 'Transitioning to Payment Elements', 'easy-digital-downloads' ),
'tooltip_desc' => __( 'You are seeing this option because your store has been using Card Elements prior to the EDD Stripe 2.9.0 update.<br /><br />To ensure that we do not affect your current checkout experience, you can use this setting to toggle between the Card Elements (legacy) and Payment Elements (updated version) to ensure that any customizations or theming you have done still function properly.<br /><br />Please be advised, that in a future version of the Stripe extension, we will deprecate the Card Elements, so take this time to update your store!', 'easy-digital-downloads' ),
);
}
$stripe_settings['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',
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
);
$radar_rules_url = sprintf(
'https://dashboard.stripe.com%s/settings/radar/rules',
edd_is_test_mode() ? '/test' : ''
);
$stripe_settings['stripe_allow_prepaid_elements_note'] = array(
'id' => 'stripe_allow_prepaid_elements_note',
'name' => __( 'Prepaid Cards', 'easy-digital-downloads' ),
'desc' => sprintf(
__( 'Prepaid card allowance can now be managed in your <a href="%s" target="_blank">Stripe Radar Rules</a>.', 'easy-digital-downloads' ),
$radar_rules_url
),
'type' => 'descriptive_text',
'class' => 'payment-elements' === $elements_mode ? 'payment-elements-feature' : 'edd-hidden payment-elements-feature',
);
$stripe_settings['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',
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
);
$stripe_settings['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',
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
);
$stripe_settings['stripe_use_existing_cards_elements_note'] = array(
'id' => 'stripe_use_existing_cards_elements_note',
'name' => __( 'Show Previously Used Cards', 'easy-digital-downloads' ),
'desc' => sprintf(
__( 'Previously used cards are now managed by <a href="%s" target="_blank">Link by Stripe</a>, for even better conversions and security.', 'easy-digital-downloads' ),
'https://link.co/'
),
'type' => 'descriptive_text',
'class' => 'payment-elements' === $elements_mode ? 'payment-elements-feature' : 'edd-hidden payment-elements-feature',
);
}
$settings['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',
),
'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,
)
)
),
);
}
// 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
);
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 );
return $settings;
}
add_filter( 'edd_settings_gateways', 'edds_add_settings' );
@ -204,11 +221,15 @@ add_filter( 'edd_settings_gateways', 'edds_add_settings' );
*
* @access public
* @since 2.5
*
* @param string $value The value currently set for the Stripe billing fields setting.
* @param string $key The Stripe setting key to detect, stripe_billing_fields.
*
* @return string
*/
function edd_stripe_sanitize_stripe_billing_fields_save( $value, $key ) {
if( 'stripe_billing_fields' == $key && edd_use_taxes() ) {
if ( 'stripe_billing_fields' === $key && edd_use_taxes() ) {
$value = 'full';
@ -223,8 +244,8 @@ add_filter( 'edd_settings_sanitize_select', 'edd_stripe_sanitize_stripe_billing_
* 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
* @param string $html The full html for the setting output.
* @param array $args The original arguments passed in to output the html.
*
* @return string
*/
@ -264,7 +285,7 @@ function edd_stripe_connect_notice_callback( $args ) {
*
* @since 2.7.0
*
* @param array $args The setting field arguments
* @param array $args The setting field arguments.
*/
function edd_stripe_checkout_notice_callback( $args ) {
$value = isset( $args['desc'] ) ? $args['desc'] : '';
@ -280,7 +301,7 @@ function edd_stripe_checkout_notice_callback( $args ) {
* @since 2.8.1
*/
function edd_stripe_requirements_not_met_callback() {
$required_version = 5.6;
$required_version = 7.1;
$current_version = phpversion();
echo '<div class="notice inline notice-warning">';
@ -299,7 +320,7 @@ function edd_stripe_requirements_not_met_callback() {
),
array(
'code' => true,
'strong' => true
'strong' => true,
)
);
echo '</p>';
@ -338,7 +359,7 @@ function edd_stripe_requirements_not_met_callback() {
* @since 2.8.1
*
* @param string $html Setting HTML.
* @param array $args Setting arguments.
* @param array $args Setting arguments.
* @return string
*/
function edds_payment_gateways_notice( $html, $args ) {
@ -353,7 +374,7 @@ function edds_payment_gateways_notice( $html, $args ) {
return $html;
}
$required_version = 5.6;
$required_version = 7.1;
$current_version = phpversion();
$html .= '<div id="edds-payment-gateways-stripe-unmet-requirements" class="notice inline notice-info"><p>' .
@ -371,7 +392,7 @@ function edds_payment_gateways_notice( $html, $args ) {
),
array(
'code' => true,
'strong' => true
'strong' => true,
)
) .
'</p><p><strong>' .

View File

@ -13,21 +13,6 @@ 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.
*
@ -158,12 +143,16 @@ function edds_process_gateway_connect_completion() {
$redirect_url = admin_url( 'edit.php?post_type=download&page=edd-settings&tab=gateways&section=edd-stripe' );
if ( ! empty( $redirect_screen ) ) {
switch ( $redirect_screen ) {
case 'onboarding-wizard':
$redirect_url = admin_url( 'edit.php?post_type=download&page=edd-onboarding-wizard&current_step=payment_methods' );
break;
}
if ( ! empty( $redirect_screen ) && 'onboarding-wizard' === $redirect_screen ) {
$redirect_url = edd_get_admin_url(
array(
'page' => 'edd-onboarding-wizard',
'current_step' => 'payment_methods',
)
);
$gateways = edd_get_option( 'gateways', array() );
$gateways['stripe'] = true;
edd_update_option( 'gateways', $gateways );
}
wp_safe_redirect( esc_url_raw( $redirect_url ) );
@ -355,7 +344,7 @@ function edds_stripe_connect_setting_field() {
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 href="' . esc_url( edds_documentation_route( 'stripe' ) ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
),
array(
@ -572,6 +561,15 @@ function edds_stripe_connect_account_info_ajax_response() {
'Pay as you go pricing: 2% per-transaction fee + Stripe fees.',
'easy-digital-downloads'
)
. ' ' .
sprintf(
/** Translators: Replacements are for the html wrappers for the phrse Upgrade to Pro and should not be translated. */
esc_html__(
'%sUpgrade to Pro%s and install the Stripe Pro Gateway to remove transaction fees.', 'easy-digital-downloads'
),
'<span class="edd-pro-upgrade"><a href="' . edd_link_helper( 'https://easydigitaldownloads.com/pricing/', array( 'utm_medium' => 'stripe-settings', 'utm_content' => 'upgrade-to-pro' ) ) . '" target="_blank">',
'</a></span>'
)
)
: '';
@ -680,6 +678,20 @@ function edds_stripe_connect_admin_notices_register() {
);
try {
// Stripe Connect - Manually managed keys.
$registry->add(
'stripe-connect-manual',
array(
'message' => sprintf(
'<p>%s</p><p>%s</p>',
esc_html__( 'Your current Stripe payment connection is out of date. Enable more secure and reliable payments by clicking the button below to enable Stripe Connect.', 'easy-digital-downloads' ),
$connect_button
),
'type' => 'error',
'dismissible' => true,
)
);
// Stripe Connect.
$registry->add(
'stripe-connect',
@ -716,6 +728,7 @@ function edds_stripe_connect_admin_notices_register() {
'dismissible' => true,
)
);
} catch( Exception $e ) {
return new WP_Error( 'edds-invalid-notices-registration', esc_html__( $e->getMessage() ) );
};
@ -764,7 +777,11 @@ function edds_stripe_connect_admin_notices_print() {
// Stripe Connect.
if ( false === $mode_toggle ) {
$notices->output( 'stripe-connect' );
if ( edds_stripe_connect_can_manage_keys() ) {
$notices->output( 'stripe-connect-manual' );
} else {
$notices->output( 'stripe-connect' );
}
// Stripe Connect reconnect.
} else {
$notices->output( 'stripe-connect-reconnect' );

View File

@ -23,6 +23,38 @@ add_action( 'admin_init', function() {
}
} );
/**
* Automatic 'upgrade' to detect an existing Stripe install to keep them on card-elements.
*
* Since people can customize the checkout experiance, we need to keep existing installs on card
* elements, and let them manually migrate to the payment elements integration. This sets an option
* for the elements mode, and if the site is already set up, defines an option to label the fact that
* they still have access to swap between card and payment elements.
*
* @since 2.9.0
*/
add_action( 'admin_init', function() {
// If the elements mode exists, don't run this again.
if ( false !== edd_get_option( 'stripe_elements_mode' ) ) {
return;
}
$elements_mode = 'payment-elements';
if (
edds_stripe_connect_can_manage_keys() ||
! empty( edd_get_option( 'stripe_connect_account_id', false ) )
) {
$elements_mode = 'card-elements';
}
edd_update_option( 'stripe_elements_mode', $elements_mode );
if ( 'card-elements' === $elements_mode ) {
add_option( '_edds_legacy_elements_enabled', 1, false );
}
} );
/**
* Stripe Upgrade Notices
*

View File

@ -91,6 +91,10 @@ class EDD_Stripe {
require_once EDDS_PLUGIN_DIR . '/includes/class-stripe-api.php';
// We need this one to load early so we can use it in the upcoming includes.
require_once EDDS_PLUGIN_DIR . '/includes/elements/functions.php';
$elements_mode = edds_get_elements_mode();
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';
@ -108,15 +112,41 @@ class EDD_Stripe {
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';
// Payment Actions, separated by elements type.
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions/functions.php';
switch ( $elements_mode ) {
case 'card-elements':
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions/card-elements-actions.php';
break;
case 'payment-elements':
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions/payment-elements-actions.php';
break;
}
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';
// Load Apple Pay functions.
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/apple-pay.php';
// Stripe Elements, separated by elements type.
switch ( $elements_mode ) {
case 'card-elements':
require_once EDDS_PLUGIN_DIR . '/includes/elements/card-elements.php';
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/index.php';
break;
case 'payment-elements':
require_once EDDS_PLUGIN_DIR . '/includes/elements/payment-elements.php';
break;
}
// 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() ) {
@ -129,7 +159,6 @@ class EDD_Stripe {
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 ) {

View File

@ -158,15 +158,9 @@ function _edds_get_purchase_form_user( $valid_data = array() ) {
}
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'] );
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
if ( $customer ) {
edd_maybe_add_customer_address( $customer->id, $user['address'] );
}
}
@ -192,6 +186,9 @@ function _edds_get_purchase_form_user( $valid_data = array() ) {
* @since 2.7.0
*/
function _edds_process_purchase_form() {
// Unset any Errors so they aren't left over form other attempts.
edd_clear_errors();
// Catch exceptions at a high level.
try {
// `edd_process_purchase_form()` and subsequent code executions are written
@ -220,7 +217,7 @@ function _edds_process_purchase_form() {
do_action( 'edd_pre_process_purchase' );
// Make sure the cart isn't empty.
if ( ! edd_get_cart_contents() && ! edd_cart_has_fees() ) {
if ( empty( EDD()->cart->contents ) && empty( EDD()->cart->fees) ) {
throw new \Exception( esc_html__( 'Your cart is empty.', 'easy-digital-downloads' ) );
}
@ -305,15 +302,9 @@ function _edds_process_purchase_form() {
);
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'] );
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
if ( $customer ) {
edd_maybe_add_customer_address( $customer->id, $user['address'] );
}
}
@ -389,53 +380,72 @@ function _edds_process_purchase_form() {
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' ) ) {
/**
* WordPress core function polyfill for WordPress 5.4.
*
* @since 2.9.0
*/
if ( ! function_exists( 'wp_get_environment_type' ) ) {
/**
* Check the network site URL for signs of being a development environment.
* Retrieves the current environment type.
*
* @since 3.0
* The type can be set via the `WP_ENVIRONMENT_TYPE` global system variable,
* or a constant of the same name.
*
* @return bool $retval True if dev, false if not.
* Possible values are 'local', 'development', 'staging', and 'production'.
* If not set, the type defaults to 'production'.
*
* @return string The current environment type.
*/
function edd_is_dev_environment() {
function wp_get_environment_type() {
static $current_env = '';
// Assume not a development environment
$retval = false;
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && $current_env ) {
return $current_env;
}
// 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',
$wp_environments = array(
'local',
'development',
'staging',
'production',
);
// Loop through all strings
foreach ( $strings as $string ) {
if ( stristr( $network_url, $string ) ) {
$retval = $string;
break;
// Add a note about the deprecated WP_ENVIRONMENT_TYPES constant.
if ( defined( 'WP_ENVIRONMENT_TYPES' ) && function_exists( '_deprecated_argument' ) ) {
if ( function_exists( '__' ) ) {
/* translators: %s: WP_ENVIRONMENT_TYPES */
$message = sprintf( __( 'The %s constant is no longer supported.' ), 'WP_ENVIRONMENT_TYPES' );
} else {
$message = sprintf( 'The %s constant is no longer supported.', 'WP_ENVIRONMENT_TYPES' );
}
_deprecated_argument(
'define()',
'5.5.1',
$message
);
}
// Check if the environment variable has been set, if `getenv` is available on the system.
if ( function_exists( 'getenv' ) ) {
$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
if ( false !== $has_env ) {
$current_env = $has_env;
}
}
// Filter & return
return apply_filters( 'edd_is_dev_environment', $retval );
// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE ) {
$current_env = WP_ENVIRONMENT_TYPE;
}
// Make sure the environment is an allowed one, and not accidentally set to an invalid value.
if ( ! in_array( $current_env, $wp_environments, true ) ) {
$current_env = 'production';
}
return $current_env;
}
}

View File

@ -70,3 +70,111 @@ function edds_register_gateway( $gateways ) {
return edd_stripe()->register_gateway( $gateways );
}
/**
* Process refund in Stripe, in EDD 2.x
* For EDD 3.0, see `edd_stripe_maybe_refund_charge()`
* @see edd_stripe_maybe_refund_charge()
*
* @access public
* @since 1.8
* @deprecated 2.9.0
* @return void
*/
function edd_stripe_process_refund( $payment_id, $new_status, $old_status ) {
_edd_deprecated_function(
__FUNCTION__,
'2.9.0',
'edd_stripe_maybe_refund_charge',
debug_backtrace()
);
}
/**
* Load our admin javascript
*
* @access public
* @since 1.8
* @deprecated 2.9.0 - Deprecated as 2.9.0 requires EDD 3.1+
* @return void
*/
function edd_stripe_admin_js( $payment_id = 0 ) {
/**
* Since Stripe 2.9.0 requires EDD 3.1, we no longer need to load this.
*/
_edd_deprecated_function(
__FUNCTION__,
'2.9.0',
null,
debug_backtrace()
);
}
/**
* Display the payment status filters
*
* @since 1.6
* @deprecated 2.9.0
* @param array $views The array of views for the payments/orders table.
* @return array
*/
function edds_payment_status_filters( $views ) {
_edd_deprecated_function(
__FUNCTION__,
'2.9.0',
null,
debug_backtrace()
);
}
/**
* Renamed Apple Pay functions after we removed "prb" from them.
*
* Most of these were used internally only, and were hooked into display notices.
* No real documetnation is needed here.
*/
function edds_prb_apple_pay_admin_notices_register() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_admin_notices_register' );
return edds_apple_pay_admin_notices_register();
}
function edds_prb_apple_pay_get_fileinfo() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_get_fileinfo' );
return edds_apple_pay_get_fileinfo();
}
function edds_prb_apple_pay_is_valid() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_is_valid' );
return edds_apple_pay_is_valid();
}
function edds_prb_apple_pay_has_domain_verification_file() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_has_domain_verification_file' );
return edds_apple_pay_has_domain_verification_file();
}
function edds_prb_apple_pay_has_domain_verification() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_has_domain_verification' );
return edds_apple_pay_has_domain_verification();
}
function edds_prb_apple_pay_create_directory_and_move_file() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_create_directory_and_move_file' );
edds_apple_pay_create_directory_and_move_file();
}
function edds_prb_apple_pay_check_domain() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_check_domain' );
edds_apple_pay_check_domain();
}
function edds_prb_apple_pay_verify_domain() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_verify_domain' );
edds_apple_pay_verify_domain();
}
function edds_prb_apple_pay_admin_notices_print() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_admin_notices_print' );
edds_apple_pay_admin_notices_print();
}

View File

@ -1,11 +1,17 @@
<?php
/**
* Stripe Elements functionality.
* Stripe Card Elements functionality.
*
* @package EDD_Stripe
* @since 2.7.0
*/
/**
* Legacy Card Elements helper functions
*
* Note: These are not used in the Payment Elements integration.
*/
/**
* Retrieves the styles passed to the Stripe Elements instance.
*
@ -76,3 +82,21 @@ function edds_get_stripe_elements_options() {
return $elements_options;
}
/**
* Add the Card Elements values to the Stripe Localized variables.
*
* @since 2.9.0
*
* @param array $stripe_vars The array of values to localize for Stripe.
*
* @return array Includes any Card Elements values to the array of localized variables.
*/
function edds_card_element_js_vars( $stripe_vars ) {
$stripe_vars['elementsOptions'] = edds_get_stripe_elements_options();
$stripe_vars['elementsSplitFields'] = '1' === edd_get_option( 'stripe_split_payment_fields', false ) ? 'true' : 'false';
$stripe_vars['checkoutHasPaymentRequest'] = edds_prb_is_enabled( 'checkout' ) ? 'true' : 'false';
return $stripe_vars;
}
add_filter( 'edd_stripe_js_vars', 'edds_card_element_js_vars', 10, 1 );

View File

@ -0,0 +1,64 @@
<?php
/**
* 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;
}
/**
* Get the current elements mode.
*
* If the user is gated into the legacy mode, set the default to card-elements.
*
* @since 2.9.0
*
* @return string The elements mode string.
*/
function edds_get_elements_mode() {
$default = _edds_legacy_elements_enabled() ? 'card-elements' : 'payment-elements';
/**
* Because we use the deferred payment intents beta, only connected accounts can use Payment Elements
* for now, so we'll force them to be in `card-elements`.
*/
if ( edds_stripe_connect_can_manage_keys() ) {
return 'card-elements';
}
/**
* Recurring Subscription payment method updates need to still run card elements for now.
*/
if (
function_exists( 'edd_recurring' ) &&
( isset( $_GET['action'] ) && 'update' === $_GET['action'] ) &&
( isset( $_GET['subscription_id'] ) && is_numeric( $_GET['subscription_id'] ) )
) {
return 'card-elements';
}
return edd_get_option( 'stripe_elements_mode', $default );
}
/**
* INTERNAL ONLY: Determines if the user is gated into using the legacy card-elements.
*
* This is a transitionary function, intentded to allow us to later remove it. Do not
* use this function in any extending of EDD or Stripe.
*
* @since 2.9.0
*
* @return bool If the user is gated into using the legacy card-elements.
*/
function _edds_legacy_elements_enabled() {
return get_option( '_edds_legacy_elements_enabled', false );
}

View File

@ -0,0 +1,262 @@
<?php
/**
* Payments Elements helper functions
*
* Note: These are not used in the Card Elements integration.
*/
/**
* Retreives and allows filtering the payout elements theme.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_theme() {
$payment_elements_theme = 'none';
/**
* Filters the theme used for the Payment Elements object
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api#theme
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $payment_elements_theme The theme to use for the Payment Element object.
*/
return apply_filters( 'edds_stripe_payment_elements_theme', $payment_elements_theme );
}
/**
* Retreives and allows filtering the payout elements variables.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_variables() {
$payment_elements_variables = array();
/**
* Filters the variables used for the Payment Elements object
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api?platform=web#variables
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $payment_elements_variables Variables used for the Payment Elements.
*/
return apply_filters( 'edds_stripe_payment_elements_variables', $payment_elements_variables );
}
/**
* Retreives and allows filtering the payout elements rules.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_rules() {
$payment_elements_rules = array();
/**
* Filters the rules used for the Payment Elements object
*
* @example
* To match styles as closely as possible, EDD's imlementation does not use base 'border' declartations in our rules.
* If you want to customize the border behavior of inputs, you need to delcare a rule for each cardinal location:
* borderTop, borderRight, borderBottom, and borderLeft, with the full border color, width and style. The border radius
* is defined as borderRadius.
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api?platform=web#rules
*
* @param array $payment_elements_rules Rules used for the Payment Elements.
*/
return apply_filters( 'edds_stripe_payment_elements_rules', $payment_elements_rules );
}
/**
* Retreives and allows filtering the layout array sent to the Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_layout() {
$payment_elements_layout = array(
'type' => 'tabs',
'defaultCollapsed' => false,
);
/**
* Filters the layout variables passed to the Stripe Payment Elements.
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/elements_object/create_payment_element#payment_element_create-options-layout
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $payment_elements_layout Layout values used to create Stripe Elements object.
*/
return apply_filters( 'edds_stripe_payment_elements_layout', $payment_elements_layout );
}
/**
* Retreives and allows filtering the wallets sent to the Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_wallets() {
$default_wallet_behavior = 'auto';
/**
* Allows the ability to completely disable wallets (Google Pay, Apple Pay, etc) with a single filter.
*
* @example
* add_filter( 'edds_stripe_payment_elements_disable_wallets', '__return_true' );
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/elements_object/create_payment_element#payment_element_create-options-wallets
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param bool If wallets should be disabled.
*/
if ( apply_filters( 'edds_stripe_payment_elements_disable_wallets', false ) ) {
$default_wallet_behavior = 'never';
}
$enabled_wallets = array(
'applePay' => $default_wallet_behavior,
'googlePay' => $default_wallet_behavior,
);
/**
* Filters the wallets that the Payment Element will load.
*
* If you want to disable these, set their values to `never`
*
* @example
* array(
* 'applePay' => 'never',
* 'googlePay' => 'auto',
* )
*
* This uses an array_merge to ensure that if someone supplies a partial array we don't lose the default behavior.
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/elements_object/create_payment_element#payment_element_create-options-wallets
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $enabled_wallets Allowed wallets payment methods ot use on the Payment Element.
*/
$enabld_wallets = array_merge( $enabled_wallets, apply_filters( 'edds_stripe_payment_elements_wallets', $enabled_wallets ) );
return $enabld_wallets;
}
/**
* Retreives and allows filtering the label style sent to the Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_label_style() {
$label_style = 'above';
/**
* Filters the label appearance option.
*
* This can be set to either 'above' or 'floating'
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api?platform=web#others
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $label_style The style to use for the Payment Elements labels.
*/
return apply_filters( 'edds_stripe_payment_elements_label_style', $label_style );
}
/**
* Allows passing custom fonts into the Stripe Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_fonts() {
$fonts = array();
/**
* Allows passing custom font objects into the Stripe Elements.
*
* This either needs to be a CSS font soruce object, or a custom font source object.
* You can see the format and requiremnts for these in the links below. We default to none.
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/appendix/css_font_source_object
* @link https://stripe.com/docs/js/appendix/custom_font_source_object
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $fonts The style to use for the Payment Elements labels.
*/
return apply_filters( 'edds_stripe_payment_elements_fonts', $fonts );
}
/**
* Gathers all the possible customizations for the Stripe Payment Elements.
*
* Pulls in the filtered customizations for the theme, variables, rules, and layout items
* for the Payment Elements instantiation. This allows developers to make customizations.
*
* EDD does attempt to match the input styles on the checkout already, in the Javascript.
*
* @since 2.9.0
*
* @returns array $customizations The array of customizations.
*/
function edds_gather_payment_element_customizations() {
$customizations = array(
'theme' => edds_get_stripe_payment_elements_theme(),
'variables' => edds_get_stripe_payment_elements_variables(),
'rules' => edds_get_stripe_payment_elements_rules(),
'layout' => edds_get_stripe_payment_elements_layout(),
'wallets' => edds_get_stripe_payment_elements_wallets(),
'labels' => edds_get_stripe_payment_elements_label_style(),
'fonts' => edds_get_stripe_payment_elements_fonts(),
'i18n' => array(
'errorMessages' => edds_get_localized_error_messages(),
),
);
if ( function_exists( 'edd_recurring' ) ) {
$customizations['cartHasSubscription'] = edd_recurring()->cart_contains_recurring() ? 'true' : 'false';
}
return $customizations;
}
/**
* Add the Payment Elements values to the Stripe Localized variables.
*
* @since 2.9.0
*
* @param array $stripe_vars The array of values to localize for Stripe.
*
* @return array Includes any Payment Elements values to the array of localized variables.
*/
function edds_payment_element_js_vars( $stripe_vars ) {
$stripe_vars['elementsCustomizations'] = edds_gather_payment_element_customizations();
return $stripe_vars;
}
add_filter( 'edd_stripe_js_vars', 'edds_payment_element_js_vars', 10, 1 );

View File

@ -75,11 +75,11 @@ function edds_is_gateway_active() {
function edds_has_met_requirements( $requirement = false ) {
$requirements = array(
'php' => (
version_compare( PHP_VERSION, '5.6.0', '>' )
version_compare( PHP_VERSION, '7.1', '>' )
),
'edd' => (
defined( 'EDD_VERSION' )
? version_compare( EDD_VERSION, '2.11', '>=' )
? version_compare( EDD_VERSION, '3.1', '>=' )
: true
),
'recurring' => (
@ -88,7 +88,7 @@ function edds_has_met_requirements( $requirement = false ) {
: true
),
'wp' => (
version_compare( get_bloginfo( 'version' ), '4.9', '>=' )
version_compare( get_bloginfo( 'version' ), '5.4', '>=' )
),
);
@ -147,6 +147,12 @@ function edds_truthy_to_bool( $truthy_value ) {
*/
function edd_stripe_existing_cards_enabled() {
$use_existing_cards = edd_get_option( 'stripe_use_existing_cards', false );
// Payment Elements doesn't support existing cards.
if ( 'payment-elements' === edds_get_elements_mode() ) {
$use_existing_cards = false;
}
return ! empty( $use_existing_cards );
}
@ -567,7 +573,7 @@ function edds_verify( $nonce = 'edd-process-checkout-nonce', $action = 'edd-proc
* @return string
*/
function edds_documentation_route( $type ) {
$base_url = 'https://docs.easydigitaldownloads.com/standard';
$base_url = 'https://easydigitaldownloads.com/docs';
/**
* Filter to change EDD-Stripe support url.
@ -713,8 +719,7 @@ function edd_refund_stripe_purchase( $order_id_or_object, $refund_object = null
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' ) ) {
if ( $refund_object instanceof Order ) {
edd_add_order_transaction( array(
'object_id' => $refund_object->id,
'object_type' => 'order',
@ -771,4 +776,4 @@ function edds_get_tokenizer_input( $custom_id = '' ) {
esc_attr( $timestamp ),
esc_attr( \EDD\Utils\Tokenizer::tokenize( $timestamp ) )
);
}
}

View File

@ -49,13 +49,17 @@ function edds_process_post_data( $purchase_data ) {
return;
}
if ( isset( $_POST['edd_stripe_existing_card'] ) && 'new' !== $_POST['edd_stripe_existing_card'] ) {
return;
}
$elements_mode = edds_get_elements_mode();
// These are card-elements validations.
if ( 'card-elements' === $elements_mode ) {
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' ) );
// 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' );

View File

@ -25,48 +25,51 @@ function edds_get_localized_error_messages() {
$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,
'incomplete' => __( 'Payment processing cancelled; your order is not yet complete.', 'easy-digital-downloads' ),
'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' ),
'generic_decline' => $generic_different_payment_method,
'payment_intent_payment_attempt_failed' => $generic_different_payment_method,
'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,
);
/**
@ -96,14 +99,18 @@ function edds_get_localized_error_messages() {
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 ] ) ) {
if (
( 'card_declined' === $error_code || 'payment_intent_payment_attempt_failed' === $error_code || 'payment_intent_payment_attempt_failed' === $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() ) {
if ( 'local' === wp_get_environment_type() ) {
$error_message .= ' Error Code: ' . $error_code;
if ( false !== $decline_code ) {
$error_message .= ' Decline Code: ' . $decline_code;
@ -112,3 +119,17 @@ function edds_get_localized_error_message( $error_code, $error_message, $decline
return $error_message;
}
/**
* Returns a translatable string for informing the user that carts can only contain a single subscription and not be 'mixed'.
*
* @since 2.9.1
*
* @return string The translatable string for the single subscription requirement error.
*/
function edds_get_single_subscription_cart_error() {
return esc_html__(
'Subscriptions must be purchased individually. Please update your cart to only contain a single subscription.',
'easy-digital-downloads'
);
}

View File

@ -23,7 +23,7 @@ function edds_auto_register_login_user( $maybe_login ) {
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 ) ) {
if ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'edds_create_payment', 'edds_complete_payment', 'edds_create_and_complete_order' ), true ) ) {
return true;
}

View File

@ -821,6 +821,7 @@ function edds_create_payment() {
// Ensure $_COOKIE is available without a new HTTP request.
if ( class_exists( 'EDD_Auto_Register' ) ) {
add_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
add_filter( 'edd_get_option_edd_auto_register_complete_orders_only', '__return_false' );
}
// Record the pending payment.
@ -1157,60 +1158,6 @@ function edds_complete_payment_authorization() {
add_action( 'wp_ajax_edds_complete_payment_authorization', 'edds_complete_payment_authorization' );
add_action( 'wp_ajax_nopriv_edds_complete_payment_authorization', 'edds_complete_payment_authorization' );
/**
* Sets up a \Stripe\Customer object based on the current purchase data.
*
* @param array $purchase_data {
*
* }
* @return \Stripe\Customer|false $customer Stripe Customer if one is created or false on error.
*/
function edds_checkout_setup_customer( $purchase_data ) {
$customer = false;
$stripe_customer_id = '';
if ( is_user_logged_in() ) {
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
}
if ( empty( $stripe_customer_id ) ) {
// No customer ID found, let's look one up based on the email.
$stripe_customer_id = edds_get_stripe_customer_id( $purchase_data['user_email'], false );
}
$customer_name = '';
if ( ! empty( $purchase_data['user_info']['first_name'] ) ) {
$customer_name .= sanitize_text_field( $purchase_data['user_info']['first_name'] );
}
if ( ! empty( $purchase_data['user_info']['last_name'] ) ) {
$customer_name .= ' ' . sanitize_text_field( $purchase_data['user_info']['last_name'] );
}
$customer_args = array(
'email' => $purchase_data['user_email'],
'description' => $purchase_data['user_email'],
'name' => $customer_name,
);
/**
* 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, $purchase_data );
$customer = edds_get_stripe_customer( $stripe_customer_id, $customer_args );
return $customer;
}
/**
* Generates a description based on the cart details.
*
@ -1244,222 +1191,3 @@ function edds_get_payment_description( $cart_details ) {
return html_entity_decode( $purchase_summary, ENT_COMPAT, 'UTF-8' );
}
/**
* Charge a preapproved payment
*
* @since 1.6
* @return bool
*/
function edds_charge_preapproved( $payment_id = 0 ) {
$retval = false;
if ( empty( $payment_id ) ) {
return $retval;
}
$payment = edd_get_payment( $payment_id );
$customer_id = $payment->get_meta( '_edds_stripe_customer_id' );
if ( empty( $customer_id ) ) {
return $retval;
}
if ( ! in_array( $payment->status, array( 'preapproval', 'preapproval_pending' ), true ) ) {
return $retval;
}
$setup_intent_id = $payment->get_meta( '_edds_stripe_setup_intent_id' );
try {
if ( edds_is_zero_decimal_currency() ) {
$amount = edd_get_payment_amount( $payment->ID );
} else {
$amount = edd_get_payment_amount( $payment->ID ) * 100;
}
$cart_details = edd_get_payment_meta_cart_details( $payment->ID );
$purchase_summary = edds_get_payment_description( $cart_details );
$statement_descriptor = edds_get_statement_descriptor();
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = substr( $purchase_summary, 0, 22 );
}
$statement_descriptor = apply_filters( 'edds_preapproved_statement_descriptor', $statement_descriptor, $payment->ID );
$statement_descriptor = edds_sanitize_statement_descriptor( $statement_descriptor );
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = null;
}
// Create a PaymentIntent using SetupIntent data.
if ( ! empty( $setup_intent_id ) ) {
$setup_intent = edds_api_request( 'SetupIntent', 'retrieve', $setup_intent_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $setup_intent->payment_method,
'customer' => $setup_intent->customer,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => $setup_intent->metadata->toArray(),
'statement_descriptor' => $statement_descriptor,
);
// Process a legacy preapproval. Uses the Customer's default source.
} else {
$customer = \Stripe\Customer::retrieve( $customer_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $customer->default_source,
'customer' => $customer->id,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => array(
'email' => edd_get_payment_user_email( $payment->ID ),
'edd_payment_id' => $payment->ID,
),
'statement_descriptor' => $statement_descriptor,
);
}
/** This filter is documented in includes/payment-actions.php */
$intent_args = apply_filters( 'edds_create_payment_intent_args', $intent_args, array() );
$payment_intent = edds_api_request( 'PaymentIntent', 'create', $intent_args );
if ( 'succeeded' === $payment_intent->status ) {
$charge_id = current( $payment_intent->charges->data )->id;
$payment->status = 'publish';
$payment->add_note( 'Stripe Charge ID: ' . $charge_id );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $payment_intent->id );
$payment->add_meta( '_edds_stripe_payment_intent_id', $payment_intent->id );
$payment->transaction_id = $charge_id;
$retval = $payment->save();
}
} catch( \Stripe\Exception\ApiErrorException $e ) {
$error = $e->getJsonBody()['error'];
$payment->status = 'preapproval_pending';
$payment->add_note( esc_html(
edds_get_localized_error_message( $error['code'], $error['message'] )
) );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $error['payment_intent']['id'] );
$payment->add_meta( '_edds_stripe_payment_intent_id', $error['payment_intent']['id'] );
$payment->save();
/**
* Allows further processing when a Preapproved payment needs further action.
*
* @since 2.7.0
*
* @param int $payment_id ID of the payment.
*/
do_action( 'edds_preapproved_payment_needs_action', $payment_id );
} catch( \Exception $e ) {
$payment->add_note( esc_html( $e->getMessage() ) );
}
return $retval;
}
/**
* Process refund in Stripe, in EDD 2.x
* For EDD 3.0, see `edd_stripe_maybe_refund_charge()`
* @see edd_stripe_maybe_refund_charge()
*
* @access public
* @since 1.8
* @return void
*/
function edd_stripe_process_refund( $payment_id, $new_status, $old_status ) {
if ( empty( $_POST['edd_refund_in_stripe'] ) ) {
return;
}
$should_process_refund = 'publish' != $old_status && 'revoked' != $old_status ? false : true;
$should_process_refund = apply_filters( 'edds_should_process_refund', $should_process_refund, $payment_id, $new_status, $old_status );
if ( false === $should_process_refund ) {
return;
}
if ( 'refunded' != $new_status ) {
return;
}
try {
edd_refund_stripe_purchase( $payment_id );
} catch ( \Exception $e ) {
wp_die( $e->getMessage(), __( 'Error', 'easy-digital-downloads' ) , array( 'response' => 400 ) );
}
}
add_action( 'edd_update_payment_status', 'edd_stripe_process_refund', 200, 3 );
/**
* If selected, refunds a charge in Stripe when creating a new refund record.
* This handles refunds in EDD 3.0+. For EDD 2.x see `edd_stripe_process_refund()`
* @see edd_stripe_process_refund()
*
* @since 2.8.7
*
* @param int $order_id ID of the order we're processing a refund for.
* @param int $refund_id ID of the newly created refund record.
* @param bool $all_refunded Whether or not this was a full refund.
*/
function edd_stripe_maybe_refund_charge( $order_id, $refund_id, $all_refunded ) {
if ( ! current_user_can( 'edit_shop_payments', $order_id ) ) {
return;
}
if ( empty( $_POST['data'] ) ) {
return;
}
$order = edd_get_order( $order_id );
if ( empty( $order->gateway ) || 'stripe' !== $order->gateway ) {
return;
}
edd_debug_log( sprintf( 'Stripe - Maybe processing refund for order #%d.', $order_id ) );
// Get our data out of the serialized string.
parse_str( $_POST['data'], $form_data );
if ( empty( $form_data['edd-stripe-refund'] ) ) {
edd_debug_log( 'Stripe - Exiting refund process, as checkbox was not selected.' );
edd_add_note( array(
'object_id' => $order_id,
'object_type' => 'order',
'user_id' => is_admin() ? get_current_user_id() : 0,
'content' => __( 'Charge not refunded in Stripe, as checkbox was not selected.', 'easy-digital-downloads' )
) );
return;
}
edd_debug_log( 'Stripe - Refund checkbox was selected, proceeding to refund charge.' );
$refund = edd_get_order( $refund_id );
if ( empty( $refund->total ) ) {
edd_debug_log( sprintf(
'Stripe - Exiting refund for order #%d - refund total is empty.',
$order_id
) );
return;
}
try {
edd_refund_stripe_purchase( $order, $refund );
} catch ( \Exception $e ) {
edd_debug_log( sprintf( 'Exception thrown while refunding order #%d. Message: %s', $order_id, $e->getMessage() ) );
}
}
add_action( 'edd_refund_order', 'edd_stripe_maybe_refund_charge', 10, 3 );

View File

@ -0,0 +1,242 @@
<?php
/**
* Sets up a \Stripe\Customer object based on the current purchase data.
*
* @param array $purchase_data {
*
* }
* @return \Stripe\Customer|false $customer Stripe Customer if one is created or false on error.
*/
function edds_checkout_setup_customer( $purchase_data ) {
$customer = false;
$stripe_customer_id = '';
if ( is_user_logged_in() ) {
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
}
if ( empty( $stripe_customer_id ) ) {
// No customer ID found, let's look one up based on the email.
$stripe_customer_id = edds_get_stripe_customer_id( $purchase_data['user_email'], false );
}
$customer_name = '';
if ( ! empty( $purchase_data['user_info']['first_name'] ) ) {
$customer_name .= sanitize_text_field( $purchase_data['user_info']['first_name'] );
}
if ( ! empty( $purchase_data['user_info']['last_name'] ) ) {
$customer_name .= ' ' . sanitize_text_field( $purchase_data['user_info']['last_name'] );
}
$customer_args = array(
'email' => $purchase_data['user_email'],
'description' => $purchase_data['user_email'],
'name' => $customer_name,
);
/**
* 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, $purchase_data );
$customer = edds_get_stripe_customer( $stripe_customer_id, $customer_args );
return $customer;
}
/**
* Charge a preapproved payment
*
* @since 1.6
* @return bool
*/
function edds_charge_preapproved( $payment_id = 0 ) {
$retval = false;
if ( empty( $payment_id ) ) {
return $retval;
}
$payment = edd_get_payment( $payment_id );
$customer_id = $payment->get_meta( '_edds_stripe_customer_id' );
if ( empty( $customer_id ) ) {
return $retval;
}
if ( ! in_array( $payment->status, array( 'preapproval', 'preapproval_pending' ), true ) ) {
return $retval;
}
$setup_intent_id = $payment->get_meta( '_edds_stripe_setup_intent_id' );
try {
if ( edds_is_zero_decimal_currency() ) {
$amount = edd_get_payment_amount( $payment->ID );
} else {
$amount = edd_get_payment_amount( $payment->ID ) * 100;
}
$cart_details = edd_get_payment_meta_cart_details( $payment->ID );
$purchase_summary = edds_get_payment_description( $cart_details );
$statement_descriptor = edds_get_statement_descriptor();
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = substr( $purchase_summary, 0, 22 );
}
$statement_descriptor = apply_filters( 'edds_preapproved_statement_descriptor', $statement_descriptor, $payment->ID );
$statement_descriptor = edds_sanitize_statement_descriptor( $statement_descriptor );
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = null;
}
// Create a PaymentIntent using SetupIntent data.
if ( ! empty( $setup_intent_id ) ) {
$setup_intent = edds_api_request( 'SetupIntent', 'retrieve', $setup_intent_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $setup_intent->payment_method,
'customer' => $setup_intent->customer,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => $setup_intent->metadata->toArray(),
'statement_descriptor' => $statement_descriptor,
);
// Process a legacy preapproval. Uses the Customer's default source.
} else {
$customer = \Stripe\Customer::retrieve( $customer_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $customer->default_source,
'customer' => $customer->id,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => array(
'email' => edd_get_payment_user_email( $payment->ID ),
'edd_payment_id' => $payment->ID,
),
'statement_descriptor' => $statement_descriptor,
);
}
/** This filter is documented in includes/payment-actions.php */
$intent_args = apply_filters( 'edds_create_payment_intent_args', $intent_args, array() );
$payment_intent = edds_api_request( 'PaymentIntent', 'create', $intent_args );
if ( 'succeeded' === $payment_intent->status ) {
$charge_id = current( $payment_intent->charges->data )->id;
$payment->status = 'publish';
$payment->add_note( 'Stripe Charge ID: ' . $charge_id );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $payment_intent->id );
$payment->add_meta( '_edds_stripe_payment_intent_id', $payment_intent->id );
$payment->transaction_id = $charge_id;
$retval = $payment->save();
}
} catch( \Stripe\Exception\ApiErrorException $e ) {
$error = $e->getJsonBody()['error'];
$payment->status = 'preapproval_pending';
$payment->add_note( esc_html(
edds_get_localized_error_message( $error['code'], $error['message'] )
) );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $error['payment_intent']['id'] );
$payment->add_meta( '_edds_stripe_payment_intent_id', $error['payment_intent']['id'] );
$payment->save();
/**
* Allows further processing when a Preapproved payment needs further action.
*
* @since 2.7.0
*
* @param int $payment_id ID of the payment.
*/
do_action( 'edds_preapproved_payment_needs_action', $payment_id );
} catch( \Exception $e ) {
$payment->add_note( esc_html( $e->getMessage() ) );
}
return $retval;
}
/**
* If selected, refunds a charge in Stripe when creating a new refund record.
* This handles refunds in EDD 3.0+. For EDD 2.x see `edd_stripe_process_refund()`
* @see edd_stripe_process_refund()
*
* @since 2.8.7
*
* @param int $order_id ID of the order we're processing a refund for.
* @param int $refund_id ID of the newly created refund record.
* @param bool $all_refunded Whether or not this was a full refund.
*/
function edd_stripe_maybe_refund_charge( $order_id, $refund_id, $all_refunded ) {
if ( ! current_user_can( 'edit_shop_payments', $order_id ) ) {
return;
}
if ( empty( $_POST['data'] ) ) {
return;
}
$order = edd_get_order( $order_id );
if ( empty( $order->gateway ) || 'stripe' !== $order->gateway ) {
return;
}
edd_debug_log( sprintf( 'Stripe - Maybe processing refund for order #%d.', $order_id ) );
// Get our data out of the serialized string.
parse_str( $_POST['data'], $form_data );
if ( empty( $form_data['edd-stripe-refund'] ) ) {
edd_debug_log( 'Stripe - Exiting refund process, as checkbox was not selected.' );
edd_add_note( array(
'object_id' => $order_id,
'object_type' => 'order',
'user_id' => is_admin() ? get_current_user_id() : 0,
'content' => __( 'Charge not refunded in Stripe, as checkbox was not selected.', 'easy-digital-downloads' )
) );
return;
}
edd_debug_log( 'Stripe - Refund checkbox was selected, proceeding to refund charge.' );
$refund = edd_get_order( $refund_id );
if ( empty( $refund->total ) ) {
edd_debug_log( sprintf(
'Stripe - Exiting refund for order #%d - refund total is empty.',
$order_id
) );
return;
}
try {
edd_refund_stripe_purchase( $order, $refund );
} catch ( \Exception $e ) {
edd_debug_log( sprintf( 'Exception thrown while refunding order #%d. Message: %s', $order_id, $e->getMessage() ) );
}
}
add_action( 'edd_refund_order', 'edd_stripe_maybe_refund_charge', 10, 3 );

View File

@ -0,0 +1,721 @@
<?php
/**
* Payment actions.
*
* @package EDD_Stripe
* @since 2.7.0
*/
function edds_maybe_disable_card_name() {
// We no longer need a card name field.
add_filter( 'edd_purchase_form_required_fields', function( $required_fields ) {
unset( $required_fields['card_name'] );
return $required_fields;
} );
remove_action( 'edd_checkout_error_checks', 'edds_process_post_data' );
}
add_action( 'edd_pre_process_purchase', 'edds_maybe_disable_card_name' );
/**
* Starts the process of completing a purchase with Stripe.
*
* Generates an intent that can require user authorization before proceeding.
*
* @link https://stripe.com/docs/payments/intents
* @since 2.7.0
*
* @param array $purchase_data {
* Purchase form data.
*
* }
*/
function edds_process_purchase_form( $purchase_data ) {
// Catch a straight to gateway request.
// Remove the error set by the "gateway mismatch" and allow the redirect.
if ( isset( $_REQUEST['edd_action'] ) && 'straight_to_gateway' === $_REQUEST['edd_action'] ) {
foreach ( $purchase_data['downloads'] as $download ) {
$options = isset( $download['options'] ) ? $download['options'] : array();
$options['quantity'] = isset( $download['quantity'] ) ? $download['quantity'] : 1;
edd_add_to_cart( $download['id'], $options );
}
edd_unset_error( 'edd-straight-to-gateway-error' );
edd_send_back_to_checkout();
return;
}
try {
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'We are unable to process your payment at this time, please try again later or contact support.',
'easy-digital-downloads'
)
);
}
/**
* Allows processing before an Intent is created.
*
* @since 2.7.0
*
* @param array $purchase_data Purchase data.
*/
do_action( 'edds_pre_process_purchase_form', $purchase_data );
/**
* We need to unhook some of the Recurring Payments actions here as we're handling captures ourselves.
*
* We're also going to attempt to restrict this to a single subscription and no mixed carts, for the time being.
*/
if ( function_exists( 'edd_recurring' ) ) {
if ( ( count( edd_get_cart_contents() ) > 1 && edd_recurring()->cart_contains_recurring() ) || edd_recurring()->cart_is_mixed() ) {
throw new \EDD_Stripe_Gateway_Exception( edds_get_single_subscription_cart_error() );
}
global $edd_recurring_stripe;
remove_filter( 'edds_create_payment_intent_args', array( $edd_recurring_stripe, 'create_payment_intent_args' ), 10, 2 );
remove_filter( 'edds_capture_payment_intent', array( $edd_recurring_stripe, 'capture_payment_intent' ) );
}
if ( edds_is_zero_decimal_currency() ) {
$amount = $purchase_data['price'];
} else {
$amount = round( $purchase_data['price'] * 100, 0 );
}
$existing_intent = false;
$customer = false;
if ( ! empty( $_REQUEST['intent_id'] ) && ! empty( $_REQUEST['intent_fingerprint'] ) ) {
$intent = edds_api_request( $_REQUEST['intent_type'], 'retrieve', $_REQUEST['intent_id'] );
if ( ! empty( $intent->customer ) ) {
$existing_intent = true;
$customer = edds_get_stripe_customer( $intent->customer, array() );
}
}
// We didn't have a customer on the existing intent. Make a new one.
if ( empty( $customer ) ) {
// Retrieves or creates a Stripe Customer.
$customer = edds_checkout_setup_customer( $purchase_data );
}
if ( ! $customer ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Unable to create customer. Please try again.',
'easy-digital-downloads'
)
);
}
/**
* Allows processing before an Intent is created, but
* after a \Stripe\Customer is available.
*
* @since 2.7.0
*
* @param array $purchase_data Purchase data.
* @param \Stripe\Customer $customer Stripe Customer object.
*/
do_action( 'edds_process_purchase_form_before_intent', $purchase_data, $customer );
// Create a list of {$download_id}_{$price_id}.
$payment_items = array();
foreach ( $purchase_data['cart_details'] as $item ) {
$price_id = isset( $item['item_number']['options']['price_id'] )
? $item['item_number']['options']['price_id']
: null;
$payment_items[] = $item['id'] . ( ! empty( $price_id ) ? ( '_' . $price_id ) : '' );
}
// Shared Intent arguments.
$intent_args = array(
'customer' => $customer->id,
'metadata' => array(
'email' => esc_html( $purchase_data['user_info']['email'] ),
'edd_payment_subtotal' => esc_html( $purchase_data['subtotal'] ),
'edd_payment_discount' => esc_html( $purchase_data['discount'] ),
'edd_payment_tax' => esc_html( $purchase_data['tax'] ),
'edd_payment_tax_rate' => esc_html( $purchase_data['tax_rate'] ),
'edd_payment_fees' => esc_html( edd_get_cart_fee_total() ),
'edd_payment_total' => esc_html( $purchase_data['price'] ),
'edd_payment_items' => esc_html( implode( ', ', $payment_items ) ),
),
);
if ( ! empty( $_REQUEST['payment_method_id'] ) ) {
$intent_args['payment_method'] = sanitize_text_field( $_REQUEST['payment_method_id'] );
} else {
$payment_method_types = array(
'card',
'link',
);
$intent_args['payment_method_types'] = $payment_method_types;
}
// Create a SetupIntent for a non-payment carts.
if ( edds_is_preapprove_enabled() || 0 === $amount ) {
$intent_args = array_merge(
array(
'description' => edds_get_payment_description( $purchase_data['cart_details'] ),
'usage' => 'off_session',
),
$intent_args
);
$intent_type = 'SetupIntent';
/**
* BETA Functionality.
*
* Sending the automatic_payment_methods flag to the SetupIntent is a beta feature that we have to enable via an API version
*
* @link https://stripe.com/docs/payments/defer-intent-creation?type=setup#create-intent
*/
add_action(
'edds_pre_stripe_api_request',
function() {
\Stripe\Stripe::setApiVersion( '2018-09-24;automatic_payment_methods_beta=v1' );
},
11
);
/**
* Filters the arguments used to create a SetupIntent.
*
* @since 2.7.0
*
* @param array $intent_args SetupIntent arguments.
* @param array $purchase_data {
* Purchase form data.
*
* }
*/
$intent_args = apply_filters( 'edds_create_setup_intent_args', $intent_args, $purchase_data );
} else {
$purchase_summary = edds_get_payment_description( $purchase_data['cart_details'] );
$statement_descriptor = edds_get_statement_descriptor();
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = substr( $purchase_summary, 0, 22 );
}
$statement_descriptor = apply_filters( 'edds_statement_descriptor', $statement_descriptor, $purchase_data );
$statement_descriptor = edds_sanitize_statement_descriptor( $statement_descriptor );
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = null;
} elseif ( is_numeric( $statement_descriptor ) ) {
$statement_descriptor = edd_get_label_singular() . ' ' . $statement_descriptor;
}
$intent_args = array_merge(
array(
'amount' => $amount,
'currency' => edd_get_currency(),
'description' => $purchase_summary,
'statement_descriptor' => $statement_descriptor,
'setup_future_usage' => 'off_session',
),
$intent_args
);
$intent_type = 'PaymentIntent';
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id' );
if (
! empty( $stripe_connect_account_id ) &&
true === edds_stripe_connect_account_country_supports_application_fees()
) {
$intent_args['application_fee_amount'] = round( $amount * 0.02 );
}
/**
* Filters the arguments used to create a SetupIntent.
*
* @since 2.7.0
*
* @param array $intent_args SetupIntent arguments.
* @param array $purchase_data {
* Purchase form data.
*
* }
*/
$intent_args = apply_filters( 'edds_create_payment_intent_args', $intent_args, $purchase_data );
}
$new_fingerprint = md5( json_encode( $intent_args ) );
// Only update the intent, and process this further if we've made changes to the intent.
if ( ! empty( $_REQUEST['intent_id'] ) && ! empty( $_REQUEST['intent_fingerprint'] ) ) {
if ( hash_equals( $_REQUEST['intent_fingerprint'], $new_fingerprint ) ) {
return wp_send_json_success( array(
'intent_id' => $intent->id,
'client_secret' => $intent->client_secret,
'intent_type' => $intent_type,
'token' => wp_create_nonce( 'edd-process-checkout' ),
'intent_fingerprint' => $new_fingerprint,
'intent_changed' => 0,
) );
}
}
if ( ! empty( $existing_intent ) ) {
edds_api_request( $intent_type, 'update', $intent->id, $intent_args );
$intent = edds_api_request( $intent_type, 'retrieve', $intent->id );
} else {
$intent = edds_api_request( $intent_type, 'create', $intent_args );
}
/**
* Allows further processing after an Intent is created.
*
* @since 2.7.0
*
* @param array $purchase_data Purchase data.
* @param \Stripe\PaymentIntent|\Stripe\SetupIntent $intent Created Stripe Intent.
* @param int $payment_id EDD Payment ID.
*/
do_action( 'edds_process_purchase_form', $purchase_data, $intent );
return wp_send_json_success( array(
'intent_id' => $intent->id,
'client_secret' => $intent->client_secret,
'intent_type' => $intent_type,
'token' => wp_create_nonce( 'edd-process-checkout' ),
'intent_fingerprint' => $new_fingerprint,
'intent_changed' => 1,
) );
} catch ( \Stripe\Exception\ApiErrorException $e ) {
$error = $e->getJsonBody()['error'];
// Record error in log.
edd_record_gateway_error(
esc_html__( 'Stripe Error 002', 'easy-digital-downloads' ),
sprintf(
esc_html__( 'There was an error while processing a Stripe payment. Order data: %s', 'easy-digital-downloads' ),
wp_json_encode( $error )
),
0
);
return wp_send_json_error( array(
'message' => esc_html(
edds_get_localized_error_message( $error['code'], $error['message'] )
),
) );
// Catch gateway processing errors.
} catch ( \EDD_Stripe_Gateway_Exception $e ) {
if ( true === $e->hasLogMessage() ) {
edd_record_gateway_error(
esc_html__( 'Stripe Error 003', 'easy-digital-downloads' ),
$e->getLogMessage(),
0
);
}
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
// Catch any remaining error.
} catch( \Exception $e ) {
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
}
}
add_action( 'edd_gateway_stripe', 'edds_process_purchase_form' );
/**
* Create an \EDD\Orders\Order.
*
* @since 2.9.0
*/
function edds_create_and_complete_order() {
// Map and merge serialized `form_data` to $_POST so it's accessible to other functions.
_edds_map_form_data_to_request( $_POST );
// Simulate being in an `edd_process_purchase_form()` request.
_edds_fake_process_purchase_step();
try {
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1001: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Rate limit reached during payment creation.'
);
}
// This must happen in the Checkout flow, so validate the Checkout nonce.
if ( false === edds_verify() ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1002: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Nonce verification failed during payment creation.'
);
}
$intent_id = isset( $_REQUEST['intent_id'] ) ? $_REQUEST['intent_id'] : '';
if ( ! isset( $intent_id ) ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1003: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to retrieve Intent data during payment creation.'
);
}
$purchase_data = edd_get_purchase_session();
if ( false === $purchase_data ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1004: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to retrieve purchase data during payment creation.'
);
}
// Ensure Intent has transitioned to the correct status.
if ( 'SetupIntent' === $_REQUEST['intent_type'] ) {
$intent = edds_api_request( 'SetupIntent', 'retrieve', $intent_id );
} else {
$intent = edds_api_request( 'PaymentIntent', 'retrieve', $intent_id );
}
if ( ! in_array( $intent->status, array( 'succeeded', 'requires_capture' ), true ) ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1005: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Invalid Intent status ' . $intent->status . ' during order creation.'
);
}
$order_data = array(
'price' => $purchase_data['price'],
'date' => $purchase_data['date'],
'user_email' => $purchase_data['user_email'],
'purchase_key' => $purchase_data['purchase_key'],
'currency' => edd_get_currency(),
'downloads' => $purchase_data['downloads'],
'cart_details' => $purchase_data['cart_details'],
'user_info' => $purchase_data['user_info'],
'status' => 'pending',
'gateway' => 'stripe',
);
// Ensure $_COOKIE is available without a new HTTP request.
if ( class_exists( 'EDD_Auto_Register' ) ) {
add_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
add_filter( 'edd_get_option_edd_auto_register_complete_orders_only', '__return_false' );
}
// Record the pending order.
$order_id = edd_build_order( $order_data );
if ( false === $order_id ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1006: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to insert order record.'
);
}
// Now get the newly created order.
$order = edd_get_order( $order_id );
// Retrieve the relevant Intent.
if ( 'setup_intent' === $intent->object ) {
$intent = edds_api_request( 'SetupIntent', 'update', $intent->id, array(
'metadata' => array(
'edd_payment_id' => $order->id,
),
) );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Payment Elements - Stripe SetupIntent ID: ' . $intent->id,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
edd_add_order_meta(
$order->id,
'_edds_stripe_setup_intent_id',
$intent->id
);
} else {
$intent = edds_api_request( 'PaymentIntent', 'update', $intent->id, array(
'metadata' => array(
'edd_payment_id' => $order->id,
),
) );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Payment Elements - Stripe PaymentIntent ID: ' . $intent->id,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
edd_add_order_meta(
$order->id,
'_edds_stripe_payment_intent_id',
$intent->id
);
}
// Use Intent ID for temporary transaction ID.
// It will be updated when a charge is available.
$order_transaction_id = edd_add_order_transaction(
array(
'object_id' => $order->id,
'object_type' => 'order',
'transaction_id' => sanitize_text_field( $intent->id ),
'gateway' => 'stripe',
'status' => 'pending',
'total' => $order->total,
)
);
// Retrieves or creates a Stripe Customer.
edd_update_order_meta( $order->id, '_edds_stripe_customer_id', $intent->customer );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Stripe Customer ID: ' . $intent->customer,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
// Attach the \Stripe\Customer ID to the \EDD_Customer meta if one exists.
$edd_customer = new EDD_Customer( $purchase_data['user_email'] );
if ( $edd_customer->id > 0 ) {
$edd_customer->update_meta( edd_stripe_get_customer_key(), $intent->customer );
}
if ( class_exists( 'EDD_Auto_Register' ) ) {
remove_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
}
if ( has_action( 'edds_payment_created' ) ) {
// Load up an EDD Payment record here, in the event there is something hooking into it.
$payment = new EDD_Payment( $order->id );
/**
* Allows further processing after a payment is created.
*
* NOTE TO DEVELOPERS: Only hook into one of these complete hooks. Using both will result in
* unexpected double processing.
*
* @since 2.7.0
*
* @param \EDD_Payment $payment EDD Payment.
* @param \Stripe\PaymentIntent|\Stripe\SetupIntent $intent Created Stripe Intent.
*/
do_action( 'edds_payment_created', $payment, $intent );
}
/**
* Allows further processing after a order is created.
*
* Sends back just the Intent ID to avoid needing always retrieve
* the intent in this step, which has been transformed via JSON,
* and is no longer a \Stripe\PaymentIntent
*
* @since 2.9.0
*
* @param \EDD\Orders\Order $order EDD Order Object.
* @param \Stripe\PaymentIntent|\Stripe\SetupIntent $intent Created Stripe Intent.
*/
do_action( 'edds_order_created', $order, $intent );
// Now we need to mark the order as complete.
$final_status = edds_is_preapprove_enabled() ? 'preapproval' : 'complete';
$updated = edd_update_order_status( $order->id, $final_status );
if ( $updated ) {
if ( 'setup_intent' !== $intent['object'] ) {
$charge_id = sanitize_text_field( current( $intent['charges']['data'] )['id'] );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Stripe Charge ID: ' . $charge_id,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
edd_update_order_transaction(
$order_transaction_id,
array(
'transaction_id' => sanitize_text_field( $charge_id ),
'gateway' => 'stripe',
'status' => 'complete',
'total' => $order->total,
)
);
}
if ( has_action( 'edds_payment_complete' ) ) {
// Load up an EDD Payment record here, in the event there is something hooking into it.
$payment = new EDD_Payment( $order->id );
/**
* Allows further processing after a payment is completed.
*
* Sends back just the Intent ID to avoid needing always retrieve
* the intent in this step, which has been transformed via JSON,
* and is no longer a \Stripe\PaymentIntent
*
* NOTE TO DEVELOPERS: Only hook into one of these complete hooks. Using both will result in
* unexpected double processing.
*
* @since 2.7.0
*
* @param \EDD_Payment $payment EDD Payment.
* @param string $intent_id Stripe Intent ID.
*/
do_action( 'edds_payment_complete', $payment, $intent['id'] );
}
/**
* Allows further processing after a order is completed.
*
* Sends back just the Intent ID to avoid needing always retrieve
* the intent in this step, which has been transformed via JSON,
* and is no longer a \Stripe\PaymentIntent
*
* @since 2.9.0
*
* @param \EDD\Orders\Order $order The EDD Order object.
* @param string $intent_id Stripe Intent ID.
*/
do_action( 'edds_order_complete', $order, $intent['id'] );
// Empty cart.
edd_empty_cart();
} else {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1007: An error occurred completing the order, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to insert order record.'
);
}
return wp_send_json_success( array(
'intent' => $intent,
'order' => $order,
// Send back a new nonce because the user might have logged in via Auto Register.
'nonce' => wp_create_nonce( 'edd-process-checkout' ),
) );
// Catch gateway processing errors.
} catch ( \EDD_Stripe_Gateway_Exception $e ) {
// Increase the rate limit count when something goes wrong mid-process.
edd_stripe()->rate_limiting->increment_card_error_count();
if ( true === $e->hasLogMessage() ) {
edd_record_gateway_error(
esc_html__( 'Stripe Error', 'easy-digital-downloads' ),
$e->getLogMessage(),
0
);
}
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
// Catch any remaining error.
} catch( \Exception $e ) {
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
}
}
add_action( 'wp_ajax_edds_create_and_complete_order', 'edds_create_and_complete_order' );
add_action( 'wp_ajax_nopriv_edds_create_and_complete_order', 'edds_create_and_complete_order' );
/**
* Uptick the rate limit card error count when a failure happens.
*
* @since 2.9.0
*/
function edds_payment_elements_rate_limit_tick() {
// Increase the card error count.
edd_stripe()->rate_limiting->increment_card_error_count();
exit();
}
add_action( 'wp_ajax_edds_payment_elements_rate_limit_tick', 'edds_payment_elements_rate_limit_tick' );
add_action( 'wp_ajax_nopriv_edds_payment_elements_rate_limit_tick', 'edds_payment_elements_rate_limit_tick' );
/**
* Generates a description based on the cart details.
*
* @param array $cart_details {
*
* }
* @return string
*/
function edds_get_payment_description( $cart_details ) {
$purchase_summary = '';
if( is_array( $cart_details ) && ! empty( $cart_details ) ) {
foreach( $cart_details as $item ) {
$purchase_summary .= $item['name'];
$price_id = isset( $item['item_number']['options']['price_id'] )
? absint( $item['item_number']['options']['price_id'] )
: false;
if ( false !== $price_id ) {
$purchase_summary .= ' - ' . edd_get_price_option_name( $item['id'], $item['item_number']['options']['price_id'] );
}
$purchase_summary .= ', ';
}
$purchase_summary = rtrim( $purchase_summary, ', ' );
}
// Stripe has a maximum of 999 characters in the charge description
$purchase_summary = substr( $purchase_summary, 0, 1000 );
return html_entity_decode( $purchase_summary, ENT_COMPAT, 'UTF-8' );
}

View File

@ -2,8 +2,6 @@
/**
* 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
*/
@ -15,7 +13,7 @@
*
* @return true|WP_Error True if all notices are registered, otherwise WP_Error.
*/
function edds_prb_apple_pay_admin_notices_register() {
function edds_apple_pay_admin_notices_register() {
$registry = edds_get_registry( 'admin-notices' );
if ( ! $registry ) {
@ -25,8 +23,8 @@ function edds_prb_apple_pay_admin_notices_register() {
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', '' )
'<strong>' . esc_html__( 'Apple Pay domain verification error.', 'easy-digital-downloads' ) . '</strong><br />' .
edd_get_option( 'stripe_apple_pay_domain_error', '' )
);
$registry->add(
@ -56,14 +54,14 @@ function edds_prb_apple_pay_admin_notices_register() {
return true;
}
add_action( 'admin_init', 'edds_prb_apple_pay_admin_notices_register', 30 );
add_action( 'admin_init', 'edds_apple_pay_admin_notices_register', 30 );
/**
* Conditionally prints registered notices.
*
* @since 2.8.0
*/
function edds_prb_apple_pay_admin_notices_print() {
function edds_apple_pay_admin_notices_print() {
// Current user needs capability to dismiss notices.
if ( ! current_user_can( 'manage_options' ) ) {
return;
@ -80,15 +78,16 @@ function edds_prb_apple_pay_admin_notices_print() {
wp_enqueue_script( 'edds-admin-notices' );
try {
$error = edd_get_option( 'stripe_prb_apple_pay_domain_error', '' );
$test_mode = edd_is_test_mode();
$is_connected = edd_get_option( 'stripe_connect_account_id', '' );
$error = edd_get_option( 'stripe_apple_pay_domain_error', '' );
$test_mode = edd_is_test_mode();
if ( ! empty( $error ) && false === $test_mode ) {
if ( ! empty( $is_connected ) && ! 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' );
add_action( 'admin_notices', 'edds_apple_pay_admin_notices_print' );
/**
* Returns information associated with the name/location of the domain verification file.
@ -97,7 +96,7 @@ add_action( 'admin_notices', 'edds_prb_apple_pay_admin_notices_print' );
*
* @return array Domain verification file information.
*/
function edds_prb_apple_pay_get_fileinfo() {
function edds_apple_pay_get_fileinfo() {
$path = untrailingslashit( $_SERVER['DOCUMENT_ROOT'] );
$dir = '.well-known';
$file = 'apple-developer-merchantid-domain-association';
@ -117,10 +116,10 @@ function edds_prb_apple_pay_get_fileinfo() {
*
* @return bool True if the domain has been verified and the association file exists.
*/
function edds_prb_apple_pay_is_valid() {
function edds_apple_pay_is_valid() {
return (
edds_prb_apple_pay_has_domain_verification_file() &&
edds_prb_apple_pay_has_domain_verification()
edds_apple_pay_has_domain_verification_file() &&
edds_apple_pay_has_domain_verification()
);
}
@ -131,8 +130,8 @@ function edds_prb_apple_pay_is_valid() {
*
* @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();
function edds_apple_pay_has_domain_verification_file() {
$fileinfo = edds_apple_pay_get_fileinfo();
if ( ! @file_exists( $fileinfo['fullpath'] ) ) {
return false;
@ -148,7 +147,7 @@ function edds_prb_apple_pay_has_domain_verification_file() {
*
* @return bool True if the saved verified domain matches the current site.
*/
function edds_prb_apple_pay_has_domain_verification() {
function edds_apple_pay_has_domain_verification() {
return edd_get_option( 'stripe_prb_apple_pay_domain' ) === $_SERVER['HTTP_HOST'];
}
@ -159,14 +158,14 @@ function edds_prb_apple_pay_has_domain_verification() {
*
* @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();
function edds_apple_pay_create_directory_and_move_file() {
$file = edds_apple_pay_has_domain_verification_file();
if ( true === $file ) {
return;
}
$fileinfo = edds_prb_apple_pay_get_fileinfo();
$fileinfo = edds_apple_pay_get_fileinfo();
// Create directory if it does not exist.
if ( ! file_exists( trailingslashit( $fileinfo['path'] ) . $fileinfo['dir'] ) ) {
@ -176,7 +175,7 @@ function edds_prb_apple_pay_create_directory_and_move_file() {
}
// Move file if needed.
if ( ! edds_prb_apple_pay_has_domain_verification_file() ) {
if ( ! edds_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' ) );
}
@ -189,8 +188,13 @@ function edds_prb_apple_pay_create_directory_and_move_file() {
*
* @since 2.8.0
*/
function edds_prb_apple_pay_check_domain() {
$error = edd_get_option( 'stripe_prb_apple_pay_domain_error', '' );
function edds_apple_pay_check_domain() {
$is_connected = edd_get_option( 'stripe_connect_account_id', '' );
if ( empty( $is_connected ) ) {
return;
}
$error = edd_get_option( 'stripe_apple_pay_domain_error', '' );
if ( empty( $error ) ) {
return;
@ -201,26 +205,35 @@ function edds_prb_apple_pay_check_domain() {
foreach ( $domains->autoPagingIterator() as $domain ) {
if ( $domain->domain_name === $_SERVER['HTTP_HOST'] ) {
edd_delete_option( 'stripe_prb_apple_pay_domain_error' );
edd_delete_option( 'stripe_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 );
add_action( 'admin_init', 'edds_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() ) {
function edds_apple_pay_verify_domain() {
// If Stripe isn't connected, just return.
if ( empty( edd_get_option( 'stripe_connect_account_id', '' ) ) ) {
return;
}
// Payment Request Button is not enabled, and card-elements is used.
$elements_mode = edds_get_elements_mode();
if ( 'card-elements' === $elements_mode ) {
if ( function_exists( 'edds_prb_is_enabled' ) && false === edds_prb_is_enabled() ) {
return;
}
}
// Avoid getting caught in AJAX requests.
if ( defined( 'DOING_AJAX' ) && true === DOING_AJAX ) {
return;
@ -237,19 +250,21 @@ function edds_prb_apple_pay_verify_domain() {
}
// Current domain matches and the file exists, do nothing.
if ( true === edds_prb_apple_pay_is_valid() ) {
if ( true === edds_apple_pay_is_valid() ) {
return;
}
try {
// Create directory and move file if needed.
edds_prb_apple_pay_create_directory_and_move_file();
edds_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(
if (
empty( $stripe_connect_account_id ) || // If we don't have a stripe connect account ID
( ! empty( $stripe_connect_account_id ) && empty( edd_get_option( 'stripe_prb_apple_pay_domain') ) ) // Or if we do have a stripe connect account ID, but we haven't registered the domain for Apple Pay
) {
edds_api_request(
'ApplePayDomain',
'create',
array(
@ -274,7 +289,7 @@ function edds_prb_apple_pay_verify_domain() {
}
} catch ( \Exception $e ) {
// Set error if something went wrong.
edd_update_option( 'stripe_prb_apple_pay_domain_error', $e->getMessage() );
edd_update_option( 'stripe_apple_pay_domain_error', $e->getMessage() );
}
}
add_action( 'admin_init', 'edds_prb_apple_pay_verify_domain', 20 );
add_action( 'admin_init', 'edds_apple_pay_verify_domain', 20 );

View File

@ -14,33 +14,30 @@
function edds_buy_now_ajax_add_to_cart() {
$data = $_POST;
if ( ! isset( $data['download_id'] ) || ! isset( $data['nonce'] ) ) {
if ( empty( $data['download_id'] ) ) {
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 ) {
if ( false === edds_verify( 'nonce', 'edd-add-to-cart-' . $download_id ) ) {
return wp_send_json_error( array(
'message' => __( 'Unable to add item to cart.', 'easy-digital-downloads' ),
) );
}
// Empty cart.
edd_empty_cart();
$args = array(
'quantity' => absint( $data['quantity'] ),
'price_id' => null,
);
if ( edd_has_variable_prices( $download_id ) ) {
$args['price_id'] = absint( $data['price_id'] );
}
// Add individual item.
edd_add_to_cart( $download_id, array(
'quantity' => $quantity,
'price_id' => $price_id,
) );
edd_add_to_cart( $download_id, $args );
return wp_send_json_success( array(
'checkout' => edds_buy_now_checkout(),
@ -55,7 +52,9 @@ add_action( 'wp_ajax_nopriv_edds_add_to_cart', 'edds_buy_now_ajax_add_to_cart' )
* @since 2.8.0
*/
function edds_buy_now_ajax_empty_cart() {
edd_empty_cart();
if ( ! empty( EDD()->cart->contents ) ) {
EDD()->cart->empty_cart();
}
return wp_send_json_success();
}

View File

@ -26,14 +26,8 @@ function edds_buy_now_modal() {
// 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();
}
edd_enqueue_scripts();
edd_localize_scripts();
remove_filter( 'edd_is_checkout', '__return_true' );
@ -63,6 +57,7 @@ function edds_buy_now_checkout() {
$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() );
@ -104,6 +99,9 @@ function edds_buy_now_checkout() {
action="<?php echo esc_url( $form_action ); ?>"
method="POST"
>
<?php if ( is_user_logged_in() && ! empty( $customer['email'] ) ) : ?>
<input type="hidden" name="edd_email" id="edd-email" value="<?php echo esc_attr( $customer['email'] ); ?>" required/>
<?php else: ?>
<p>
<label class="edd-label" for="edd-email">
<?php esc_html_e( 'Email Address', 'easy-digital-downloads' ); ?>
@ -123,6 +121,7 @@ function edds_buy_now_checkout() {
<?php endif; ?>
/>
</p>
<?php endif; ?>
<?php if ( $total > 0 ) : ?>
@ -185,13 +184,15 @@ function edds_buy_now_checkout_purchase_label( $label ) {
return $label;
}
return sprintf(
$label = sprintf(
'%s - %s',
edd_currency_filter(
edd_format_amount( $total )
),
$label
);
return $label;
}
/**

View File

@ -15,6 +15,14 @@
* @return array Filtered gateway settings.
*/
function edds_prb_add_settings( $settings ) {
/**
* In Version 2.9.0, PRBs are no longer necessary as they are part of the
* Payment Element.
*/
if ( false === _edds_legacy_elements_enabled() ) {
return $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()`
@ -34,7 +42,8 @@ function edds_prb_add_settings( $settings ) {
),
);
} else {
$prb_settings = array(
$elements_mode = edds_get_elements_mode();
$prb_settings = array(
array(
'id' => 'stripe_prb',
'name' => __( 'Apple Pay/Google Pay', 'easy-digital-downloads' ),
@ -51,7 +60,7 @@ function edds_prb_add_settings( $settings ) {
? '<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 href="' . esc_url( edds_documentation_route( 'stripe-express-checkout-apple-pay-google-pay' ) ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
)
: ''
@ -81,12 +90,21 @@ function edds_prb_add_settings( $settings ) {
),
'checkout' => __( 'Checkout', 'easy-digital-downloads' ),
),
)
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
),
array(
'id' => 'stripe_prb_elements_note',
'name' => __( 'Apple Pay/Google Pay', 'easy-digital-downloads' ),
'desc' => __( 'Apple Pay and Google Pay support is now provided via the Payment Elements integration.', 'easy-digital-downloads' ),
'type' => 'descriptive_text',
'class' => 'payment-elements' === $elements_mode ? 'payment-elements-feature' : 'edd-hidden payment-elements-feature',
),
);
}
$position = array_search(
'stripe_statement_descriptor',
'stripe_use_existing_cards',
array_values( wp_list_pluck( $settings['edd-stripe'], 'id' ) ),
true
);
@ -119,7 +137,7 @@ function edd_edds_stripe_prb_taxes_callback() {
'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 href="' . esc_url( edds_documentation_route( 'stripe-express-checkout-apple-pay-google-pay' ) ) . '#edds-prb-faqs" target="_blank" rel="noopener noreferrer">',
'</a>'
),
array(

View File

@ -21,6 +21,10 @@ function edds_prb_is_enabled( $context = array() ) {
return false;
}
if ( 'payment-elements' === edds_get_elements_mode() ) {
return false;
}
// Gather allowed and enabled contexts.
$allowed_contexts = array( 'single', 'archive', 'checkout' );
$enabled_contexts = array_keys(

View File

@ -11,7 +11,6 @@ require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/checko
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';

View File

@ -26,83 +26,96 @@ function edd_stripe_js( $force_load_scripts = false ) {
return;
}
if ( function_exists( 'edd_is_checkout' ) ) {
wp_register_script(
'sandhills-stripe-js-v3',
'https://js.stripe.com/v3/',
array(),
'v3'
);
$publishable_key = NULL;
$is_checkout = edd_is_checkout() && 0 < edd_get_cart_total();
$restrict_assets = edd_get_option( 'stripe_restrict_assets', false );
if ( edd_is_test_mode() ) {
$publishable_key = edd_get_option( 'test_publishable_key', '' );
} else {
$publishable_key = edd_get_option( 'live_publishable_key', '' );
}
if ( $is_checkout || $force_load_scripts || false === $restrict_assets ) {
wp_enqueue_script( 'sandhills-stripe-js-v3' );
}
wp_register_script(
if ( $is_checkout || $force_load_scripts ) {
$publishable_key_option = edd_is_test_mode() ? 'test_publishable_key' : 'live_publishable_key';
$publishable_key = edd_get_option( $publishable_key_option, '' );
// We're going to assume Payment Elements needs to load...
$script_source = EDDSTRIPE_PLUGIN_URL . 'assets/js/build/paymentelements.min.js';
$script_deps = array(
'sandhills-stripe-js-v3',
'https://js.stripe.com/v3/',
array(),
'v3'
'jquery',
'edd-ajax',
);
// But if the user has Card Elements, we need to load that instead.
$elements_mode = edds_get_elements_mode();
if ( 'card-elements' === $elements_mode ) {
$script_source = EDDSTRIPE_PLUGIN_URL . 'assets/js/build/cardelements.min.js';
$script_deps[] = 'jQuery.payment';
}
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,
$script_source,
$script_deps,
EDD_STRIPE_VERSION . '-' . $elements_mode,
true
);
$is_checkout = edd_is_checkout() && 0 < edd_get_cart_total();
$restrict_assets = edd_get_option( 'stripe_restrict_assets', false );
wp_enqueue_script( 'edd-stripe-js' );
if ( $is_checkout || $force_load_scripts || false === $restrict_assets ) {
wp_enqueue_script( 'sandhills-stripe-js-v3' );
}
$stripe_localized_vars = array(
'publishable_key' => trim( $publishable_key ),
'isTestMode' => edd_is_test_mode() ? 'true' : 'false',
'elementsMode' => $elements_mode,
'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' => ! empty( edd_get_option( 'entity_name' ) ) ? edd_get_option( 'entity_name' ) : get_bloginfo( 'name' ),
'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' ),
'prepaid' => edd_get_option( 'stripe_allow_prepaid', false ) ? 'true' : 'false',
'successPageUri' => edd_get_success_page_uri(),
'failurePageUri' => edd_get_failed_transaction_uri(),
'debuggingEnabled' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? 'true' : 'false',
'formLoadingText' => __( 'Please wait...', 'easy-digital-downloads' ),
'cartHasSubscription' => function_exists( 'edd_recurring' ) && edd_recurring()->cart_contains_recurring() ? 'true' : 'false',
);
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',
$stripe_localized_vars
);
$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 );
wp_localize_script( 'edd-stripe-js', 'edd_stripe_vars', $stripe_vars );
}
}
}
add_action( 'wp_enqueue_scripts', 'edd_stripe_js', 100 );
/**
* Conditionally load the Stripe CSS
*
* @param bool $force_load_scripts If we should forece loading the scripts outside of checkout.
*/
function edd_stripe_css( $force_load_scripts = false ) {
if ( false === edds_is_gateway_active() ) {
return;
@ -115,57 +128,27 @@ function edd_stripe_css( $force_load_scripts = false ) {
$deps = array();
}
wp_register_style( 'edd-stripe', EDDSTRIPE_PLUGIN_URL . 'assets/css/build/app.min.css', $deps, EDD_STRIPE_VERSION );
// We're going to assume Payment Elements needs to load...
$style_src = EDDSTRIPE_PLUGIN_URL . 'assets/css/build/paymentelements.min.css';
// But if the user has Card Elements, we need to load that instead.
$elements_mode = edds_get_elements_mode();
if ( 'card-elements' === $elements_mode ) {
$style_src = EDDSTRIPE_PLUGIN_URL . 'assets/css/build/cardelements.min.css';
}
wp_register_style(
'edd-stripe',
$style_src,
$deps,
EDD_STRIPE_VERSION . '-' . $elements_mode
);
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.
*
@ -173,7 +156,7 @@ add_action( 'edd_view_order_details_before', 'edd_stripe_admin_js', 100 );
*/
function edd_stripe_connect_admin_script( $hook ) {
if( 'download_page_edd-settings' !== $hook ) {
if ( 'download_page_edd-settings' !== $hook ) {
return;
}
@ -188,11 +171,11 @@ function edd_stripe_connect_admin_script( $hook ) {
'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(),
'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' ) ),
'ajaxurl' => esc_url( admin_url( 'admin-ajax.php' ) ),
)
);
}

View File

@ -20,8 +20,6 @@ add_action( 'edd_after_cc_fields', 'edds_add_stripe_errors', 999 );
*/
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;
@ -29,37 +27,34 @@ function edds_credit_card_form( $echo = true ) {
ob_start(); ?>
<?php if ( ! wp_script_is ( 'edd-stripe-js' ) ) : ?>
<?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() ) : ?>
<?php $elements_mode = edds_get_elements_mode(); ?>
<?php if ( 'card-elements' === $elements_mode ) : ?>
<legend><?php esc_html_e( 'Credit Card Info', 'easy-digital-downloads' ); ?></legend>
<?php else: ?>
<legend><?php esc_html_e( 'Payment Info', 'easy-digital-downloads' ); ?></legend>
<?php endif; ?>
<?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
}
echo edd_get_payment_icon(
array(
'icon' => 'lock',
'width' => 18,
'height' => 28,
'classes' => array(
'edd-icon',
'edd-icon-lock',
),
)
);
?>
</span>
<span><?php _e( 'This is a secure SSL encrypted payment.', 'easy-digital-downloads' ); ?></span>
@ -67,9 +62,11 @@ function edds_credit_card_form( $echo = true ) {
<?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() ); } ?>
if ( 'card-elements' === $elements_mode ) {
$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() ); } ?>
<?php } ?>
<div class="edd-stripe-new-card" <?php if ( ! empty( $existing_cards ) ) { echo 'style="display: none;"'; } ?>>
<?php do_action( 'edd_stripe_new_card_form' ); ?>
@ -105,19 +102,63 @@ function edd_stripe_new_card_form() {
return;
}
$elements_mode = edds_get_elements_mode();
if ( 'payment-elements' === $elements_mode ) {
edds_output_payment_elements_form();
} else {
edds_output_legacy_new_card_form();
}
}
add_action( 'edd_stripe_new_card_form', 'edd_stripe_new_card_form' );
/**
* Add the element for the Stripe Payment Elements to attach to.
*
* @since 2.9.0
*/
function edds_output_payment_elements_form() {
// Payment Elements needs to not allow checking out with mixed carts or multiple subscriptions.
if ( function_exists( 'edd_recurring' ) ) {
if ( ( count( edd_get_cart_contents() ) > 1 && edd_recurring()->cart_contains_recurring() ) || edd_recurring()->cart_is_mixed() ) {
?>
<div class="edd_errors edd-alert edd-alert-info">
<p class="edd_error" id="edd_error_edd-stripe-incompatible-cart"><?php echo edds_get_single_subscription_cart_error(); ?></p>
</div>
<?php
return;
}
}
// Clear any errors that might be sitting from a previous AJAX loading of errors.
edd_clear_errors();
?>
<div id="edd-card-wrap">
<div id="edd-stripe-payment-element"></div>
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
</div>
<?php
}
/**
* Add the legacy Card Element fields for users who are still on Card Elements.
*
* @since 2.9.0
*/
function edds_output_legacy_new_card_form() {
$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>
<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">
<div id="edd-card-wrap">
<label for="edd-card-element" class="edd-label">
<?php
if ( '1' === $split ) :
@ -138,11 +179,11 @@ function edd_stripe_new_card_form() {
</div>
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
</div>
</div>
<?php if ( '1' === $split ) : ?>
<?php if ( '1' === $split ) : ?>
<div id="edd-card-details-wrap">
<div id="edd-card-details-wrap">
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
<div id="edd-card-exp-wrap">
@ -162,13 +203,13 @@ function edd_stripe_new_card_form() {
<div id="edd-stripe-card-cvc-element" class="edd-stripe-card-cvc-element"></div>
</div>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
<div id="edd-stripe-card-errors" role="alert"></div>
<div id="edd-stripe-card-errors" role="alert"></div>
<?php
<?php
/**
* Allow output of extra content before the credit card expiration field.
*
@ -180,7 +221,6 @@ function edd_stripe_new_card_form() {
*/
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
@ -800,38 +840,40 @@ function edd_stripe_zip_and_country() {
* @return void
*/
function edd_stripe_setup_billing_address_fields() {
if( ! function_exists( 'edd_use_taxes' ) ) {
if ( 'stripe' !== edd_get_chosen_gateway() || ! edd_get_cart_total() > 0 ) {
return;
}
if( edd_use_taxes() || 'stripe' !== edd_get_chosen_gateway() || ! edd_get_cart_total() > 0 ) {
$hook = 'payment-elements' === edds_get_elements_mode() || apply_filters( 'edds_address_before_payment', false ) ? 'edd_before_cc_fields' : 'edd_after_cc_fields';
if ( edd_use_taxes() ) {
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields' );
add_action( $hook, 'edd_default_cc_address_fields' );
return;
}
$display = edd_get_option( 'stripe_billing_fields', 'full' );
switch( $display ) {
switch ( $display ) {
case 'full' :
// Make address fields required
case 'full':
// Make address fields required.
add_filter( 'edd_require_billing_address', '__return_true' );
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields' );
add_action( $hook, 'edd_default_cc_address_fields' );
break;
case 'zip_country' :
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 );
add_action( $hook, 'edd_stripe_zip_and_country', 9 );
// Make Zip required
// Make Zip required.
add_filter( 'edd_purchase_form_required_fields', 'edd_stripe_require_zip_and_country' );
break;
case 'none' :
case 'none':
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields', 10 );
break;

View File

@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInita1abe13511edc0d0d2f910910593934f::getLoader();
return ComposerAutoloaderInit52360df2f3d60779e5a0ff48f14462b7::getLoader();

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita1abe13511edc0d0d2f910910593934f
class ComposerAutoloaderInit52360df2f3d60779e5a0ff48f14462b7
{
private static $loader;
@ -22,12 +22,12 @@ class ComposerAutoloaderInita1abe13511edc0d0d2f910910593934f
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInita1abe13511edc0d0d2f910910593934f', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit52360df2f3d60779e5a0ff48f14462b7', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInita1abe13511edc0d0d2f910910593934f', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit52360df2f3d60779e5a0ff48f14462b7', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita1abe13511edc0d0d2f910910593934f::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit52360df2f3d60779e5a0ff48f14462b7::getInitializer($loader));
$loader->register(true);

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInita1abe13511edc0d0d2f910910593934f
class ComposerStaticInit52360df2f3d60779e5a0ff48f14462b7
{
public static $prefixLengthsPsr4 = array (
'S' =>
@ -27,9 +27,9 @@ class ComposerStaticInita1abe13511edc0d0d2f910910593934f
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInita1abe13511edc0d0d2f910910593934f::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInita1abe13511edc0d0d2f910910593934f::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInita1abe13511edc0d0d2f910910593934f::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit52360df2f3d60779e5a0ff48f14462b7::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit52360df2f3d60779e5a0ff48f14462b7::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit52360df2f3d60779e5a0ff48f14462b7::$classMap;
}, null, ClassLoader::class);
}

View File

@ -2,17 +2,17 @@
"packages": [
{
"name": "stripe/stripe-php",
"version": "v7.47.0",
"version_normalized": "7.47.0.0",
"version": "v7.128.0",
"version_normalized": "7.128.0.0",
"source": {
"type": "git",
"url": "https://github.com/stripe/stripe-php.git",
"reference": "b51656cb398d081fcee53a76f6edb8fd5c1a5306"
"reference": "c704949c49b72985c76cc61063aa26fefbd2724e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/b51656cb398d081fcee53a76f6edb8fd5c1a5306",
"reference": "b51656cb398d081fcee53a76f6edb8fd5c1a5306",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/c704949c49b72985c76cc61063aa26fefbd2724e",
"reference": "c704949c49b72985c76cc61063aa26fefbd2724e",
"shasum": ""
},
"require": {
@ -22,13 +22,12 @@
"php": ">=5.6.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.16.1",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "^3.3",
"symfony/process": "~3.4"
"friendsofphp/php-cs-fixer": "3.5.0",
"phpstan/phpstan": "^1.2",
"phpunit/phpunit": "^5.7 || ^9.0",
"squizlabs/php_codesniffer": "^3.3"
},
"time": "2020-08-13T22:35:56+00:00",
"time": "2022-05-05T17:18:02+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -60,7 +59,7 @@
],
"support": {
"issues": "https://github.com/stripe/stripe-php/issues",
"source": "https://github.com/stripe/stripe-php/tree/v7.47.0"
"source": "https://github.com/stripe/stripe-php/tree/v7.128.0"
},
"install-path": "../stripe/stripe-php"
}

View File

@ -3,7 +3,7 @@
'name' => 'easy-digital-downloads/edd-stripe',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '24964d034941e0d3d07390ecc39570bfc64e7369',
'reference' => 'a63eec57c1f5da47c09cc9cd8cf4162f8c65652a',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -13,16 +13,16 @@
'easy-digital-downloads/edd-stripe' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '24964d034941e0d3d07390ecc39570bfc64e7369',
'reference' => 'a63eec57c1f5da47c09cc9cd8cf4162f8c65652a',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'stripe/stripe-php' => array(
'pretty_version' => 'v7.47.0',
'version' => '7.47.0.0',
'reference' => 'b51656cb398d081fcee53a76f6edb8fd5c1a5306',
'pretty_version' => 'v7.128.0',
'version' => '7.128.0.0',
'reference' => 'c704949c49b72985c76cc61063aa26fefbd2724e',
'type' => 'library',
'install_path' => __DIR__ . '/../stripe/stripe-php',
'aliases' => array(),

View File

@ -1,5 +1,410 @@
# Changelog
## 7.128.0 - 2022-05-05
* [#1282](https://github.com/stripe/stripe-php/pull/1282) API Updates
* Add support for `default_price` on `Product`
* Add support for `instructions_email` on `Refund`
## 7.127.0 - 2022-05-05
* [#1281](https://github.com/stripe/stripe-php/pull/1281) API Updates
* Add support for new resources `FinancialConnections.AccountOwner`, `FinancialConnections.AccountOwnership`, `FinancialConnections.Account`, and `FinancialConnections.Session`
* [#1278](https://github.com/stripe/stripe-php/pull/1278) Pin setup-php action version.
* [#1277](https://github.com/stripe/stripe-php/pull/1277) API Updates
* Add support for `registered_address` on `Person`
## 7.126.0 - 2022-05-03
* [#1276](https://github.com/stripe/stripe-php/pull/1276) API Updates
* Add support for new resource `CashBalance`
* Change type of `BillingPortal.Configuration.application` from `$Application` to `deletable($Application)`
* Add support for `cash_balance` on `Customer`
* Add support for `application` on `Invoice`, `Quote`, `SubscriptionSchedule`, and `Subscription`
* Add support for new value `eu_oss_vat` on enum `TaxId.type`
* [#1274](https://github.com/stripe/stripe-php/pull/1274) Fix PHPDoc on Discount for nullable properties
* [#1272](https://github.com/stripe/stripe-php/pull/1272) Allow users to pass a custom IPRESOLVE cURL option.
## 7.125.0 - 2022-04-21
* [#1270](https://github.com/stripe/stripe-php/pull/1270) API Updates
* Add support for `expire` test helper method on resource `Refund`
## 7.124.0 - 2022-04-18
* [#1265](https://github.com/stripe/stripe-php/pull/1265) API Updates
* Add support for new resources `FundingInstructions` and `Terminal.Configuration`
* Add support for `create_funding_instructions` method on resource `Customer`
* Add support for `amount_details` on `PaymentIntent`
* Add support for `customer_balance` on `PaymentMethod`
* Add support for new value `customer_balance` on enum `PaymentMethod.type`
* Add support for `configuration_overrides` on `Terminal.Location`
## 7.123.0 - 2022-04-13
* [#1263](https://github.com/stripe/stripe-php/pull/1263) API Updates
* Add support for `increment_authorization` method on resource `PaymentIntent`
* [#1262](https://github.com/stripe/stripe-php/pull/1262) Add support for updating the version of the repo
* [#1230](https://github.com/stripe/stripe-php/pull/1230) Add PHPDoc return types
* [#1242](https://github.com/stripe/stripe-php/pull/1242) Fix some PHPDoc in tests
## 7.122.0 - 2022-04-08
* [#1261](https://github.com/stripe/stripe-php/pull/1261) API Updates
* Add support for `apply_customer_balance` method on resource `PaymentIntent`
* [#1259](https://github.com/stripe/stripe-php/pull/1259) API Updates
* Add `payment_intent.partially_funded`, `terminal.reader.action_failed`, and `terminal.reader.action_succeeded` events.
## 7.121.0 - 2022-03-30
* [#1258](https://github.com/stripe/stripe-php/pull/1258) API Updates
* Add support for `cancel_action`, `process_payment_intent`, `process_setup_intent`, and `set_reader_display` methods on resource `Terminal.Reader`
* Add support for `action` on `Terminal.Reader`
## 7.120.0 - 2022-03-29
* [#1257](https://github.com/stripe/stripe-php/pull/1257) API Updates
* Add support for Search API
* Add support for `search` method on resources `Charge`, `Customer`, `Invoice`, `PaymentIntent`, `Price`, `Product`, and `Subscription`
## 7.119.0 - 2022-03-25
* [#1256](https://github.com/stripe/stripe-php/pull/1256) API Updates
* Add support for PayNow and US Bank Accounts Debits payments
* Add support for `paynow` and `us_bank_account` on `PaymentMethod`
* Add support for new values `paynow` and `us_bank_account` on enum `PaymentMethod.type`
* Add support for `failure_balance_transaction` on `Charge`
## 7.118.0 - 2022-03-23
* [#1255](https://github.com/stripe/stripe-php/pull/1255) API Updates
* Add support for `cancel` method on resource `Refund`
* Add support for new values `bg_uic`, `hu_tin`, and `si_tin` on enum `TaxId.type`
* Add `test_helpers.test_clock.advancing`, `test_helpers.test_clock.created`, `test_helpers.test_clock.deleted`, `test_helpers.test_clock.internal_failure`, and `test_helpers.test_clock.ready` events.
## 7.117.0 - 2022-03-18
* [#1254](https://github.com/stripe/stripe-php/pull/1254) API Updates
* Add support for `status` on `Card`
* [#1251](https://github.com/stripe/stripe-php/pull/1251) Add support for SearchResult objects.
* [#1249](https://github.com/stripe/stripe-php/pull/1249) Add missing constant for payment_behavior
## 7.116.0 - 2022-03-02
* [#1248](https://github.com/stripe/stripe-php/pull/1248) API Updates
* Add support for `proration_details` on `InvoiceLineItem`
## 7.115.0 - 2022-03-01
* [#1245](https://github.com/stripe/stripe-php/pull/1245) [#1247](https://github.com/stripe/stripe-php/pull/1247) API Updates
* Add support for new resource `TestHelpers.TestClock`
* Add support for `test_clock` on `Customer`, `Invoice`, `InvoiceItem`, `Quote`, `Subscription`, and `SubscriptionSchedule`
* Add support for `next_action` on `Refund`
* Add support for `konbini` on `PaymentMethod`
* [#1244](https://github.com/stripe/stripe-php/pull/1244) API Updates
* Add support for new values `bbpos_wisepad3` and `stripe_m2` on enum `Terminal.Reader.device_type`
## 7.114.0 - 2022-02-15
* [#1243](https://github.com/stripe/stripe-php/pull/1243) Add test
* [#1240](https://github.com/stripe/stripe-php/pull/1240) API Updates
* Add support for `verify_microdeposits` method on resources `PaymentIntent` and `SetupIntent`
* [#1241](https://github.com/stripe/stripe-php/pull/1241) Add generic parameter to \Stripe\Collection usages
## 7.113.0 - 2022-02-03
* [#1239](https://github.com/stripe/stripe-php/pull/1239) API Updates
* Add `REASON_EXPIRED_UNCAPTURED_CHARGE` enum value on `Refund`.
## 7.112.0 - 2022-01-25
* [#1235](https://github.com/stripe/stripe-php/pull/1235) API Updates
* Add support for `phone_number_collection` on `PaymentLink`
* Add support for new value `is_vat` on enum `TaxId.type`
## 7.111.0 - 2022-01-20
* [#1233](https://github.com/stripe/stripe-php/pull/1233) API Updates
* Add support for new resource `PaymentLink`
* Add support for `payment_link` on `Checkout.Session`
## 7.110.0 - 2022-01-13
* [#1232](https://github.com/stripe/stripe-php/pull/1232) API Updates
* Add support for `paid_out_of_band` on `Invoice`
## 7.109.0 - 2022-01-12
* [#1231](https://github.com/stripe/stripe-php/pull/1231) API Updates
* Add support for `customer_creation` on `Checkout.Session`
* [#1227](https://github.com/stripe/stripe-php/pull/1227) Update docs URLs
## 7.108.0 - 2021-12-22
* [#1226](https://github.com/stripe/stripe-php/pull/1226) Upgrade php-cs-fixer to 3.4.0.
* [#1222](https://github.com/stripe/stripe-php/pull/1222) API Updates
* Add support for `processing` on `PaymentIntent`
* [#1220](https://github.com/stripe/stripe-php/pull/1220) API Updates
## 7.107.0 - 2021-12-09
* [#1219](https://github.com/stripe/stripe-php/pull/1219) API Updates
* Add support for `metadata` on `BillingPortal.Configuration`
* Add support for `wallets` on `Issuing.Card`
## 7.106.0 - 2021-12-09
* [#1218](https://github.com/stripe/stripe-php/pull/1218) API Updates
* Add support for new values `ge_vat` and `ua_vat` on enum `TaxId.type`
* [#1216](https://github.com/stripe/stripe-php/pull/1216) Fix namespaced classes in @return PHPDoc.
* [#1214](https://github.com/stripe/stripe-php/pull/1214) Announce PHP8 support in CHANGELOG.md
## 7.105.0 - 2021-12-06
* [#1213](https://github.com/stripe/stripe-php/pull/1213) PHP 8.1 missing ReturnTypeWillChange annotations.
* As of this version, PHP 8.1 is officially supported.
## 7.104.0 - 2021-12-01
* [#1211](https://github.com/stripe/stripe-php/pull/1211) PHPStan compatibility with PHP8.x
* [#1209](https://github.com/stripe/stripe-php/pull/1209) PHPUnit compatibility with PHP 8.x
## 7.103.0 - 2021-11-19
* [#1206](https://github.com/stripe/stripe-php/pull/1206) API Updates
* Add support for new value `jct` on enum `TaxRate.tax_type`
## 7.102.0 - 2021-11-17
* [#1205](https://github.com/stripe/stripe-php/pull/1205) API Updates
* Add support for `automatic_payment_methods` on `PaymentIntent`
## 7.101.0 - 2021-11-16
* [#1203](https://github.com/stripe/stripe-php/pull/1203) API Updates
* Add support for new resource `ShippingRate`
* Add support for `shipping_options` and `shipping_rate` on `Checkout.Session`
* Add support for `expire` method on resource `Checkout.Session`
* Add support for `status` on `Checkout.Session`
## 7.100.0 - 2021-10-11
* [#1190](https://github.com/stripe/stripe-php/pull/1190) API Updates
* Add support for `klarna` on `PaymentMethod`.
## 7.99.0 - 2021-10-11
* [#1188](https://github.com/stripe/stripe-php/pull/1188) API Updates
* Add support for `list_payment_methods` method on resource `Customer`
## 7.98.0 - 2021-10-07
* [#1187](https://github.com/stripe/stripe-php/pull/1187) API Updates
* Add support for `phone_number_collection` on `Checkout.Session`
* Add support for new value `customer_id` on enum `Radar.ValueList.item_type`
* Add support for new value `bbpos_wisepos_e` on enum `Terminal.Reader.device_type`
## 7.97.0 - 2021-09-16
* [#1181](https://github.com/stripe/stripe-php/pull/1181) API Updates
* Add support for `full_name_aliases` on `Person`
## 7.96.0 - 2021-09-15
* [#1178](https://github.com/stripe/stripe-php/pull/1178) API Updates
* Add support for livemode on Reporting.ReportType
* Add support for new value `rst` on enum `TaxRate.tax_type`
## 7.95.0 - 2021-09-01
* [#1177](https://github.com/stripe/stripe-php/pull/1177) API Updates
* Add support for `future_requirements` on `Account`, `Capability`, and `Person`
* Add support for `after_expiration`, `consent`, `consent_collection`, `expires_at`, and `recovered_from` on `Checkout.Session`
## 7.94.0 - 2021-08-19
* [#1173](https://github.com/stripe/stripe-php/pull/1173) API Updates
* Add support for new value `fil` on enum `Checkout.Session.locale`
* Add support for new value `au_arn` on enum `TaxId.type`
## 7.93.0 - 2021-08-11
* [#1172](https://github.com/stripe/stripe-php/pull/1172) API Updates
* Add support for `locale` on `BillingPortal.Session`
* [#1171](https://github.com/stripe/stripe-php/pull/1171) Fix typo in docblock `CurlClient::executeStreamingRequestWithRetries`
## 7.92.0 - 2021-07-28
* [#1167](https://github.com/stripe/stripe-php/pull/1167) API Updates
* Add support for `account_type` on `BankAccount`
* Add support for new value `redacted` on enum `Review.closed_reason`
## 7.91.0 - 2021-07-22
* [#1164](https://github.com/stripe/stripe-php/pull/1164) API Updates
* Add support for new values `hr`, `ko`, and `vi` on enum `Checkout.Session.locale`
* Add support for `payment_settings` on `Subscription`
## 7.90.0 - 2021-07-20
* [#1163](https://github.com/stripe/stripe-php/pull/1163) API Updates
* Add support for `wallet` on `Issuing.Transaction`
* [#1160](https://github.com/stripe/stripe-php/pull/1160) Remove unused API error types from docs.
## 7.89.0 - 2021-07-14
* [#1158](https://github.com/stripe/stripe-php/pull/1158) API Updates
* Add support for `list_computed_upfront_line_items` method on resource `Quote`
* [#1157](https://github.com/stripe/stripe-php/pull/1157) Improve readme for old PHP versions
## 7.88.0 - 2021-07-09
* [#1152](https://github.com/stripe/stripe-php/pull/1152) API Updates
* Add support for new resource `Quote`
* Add support for `quote` on `Invoice`
* Add support for new value `quote_accept` on enum `Invoice.billing_reason`
* [#1155](https://github.com/stripe/stripe-php/pull/1155) Add streaming methods to Service infra
* Add support for `setStreamingHttpClient` and `streamingHttpClient` to `ApiRequestor`
* Add support for `getStreamingClient` and `requestStream` to `AbstractService`
* Add support for `requestStream` to `BaseStripeClient`
* `\Stripe\RequestOptions::parse` now clones its input if it is already a `RequestOptions` object, to prevent accidental mutation.
* [#1151](https://github.com/stripe/stripe-php/pull/1151) Add `mode` constants into Checkout\Session
## 7.87.0 - 2021-06-30
* [#1149](https://github.com/stripe/stripe-php/pull/1149) API Updates
* Add support for `wechat_pay` on `PaymentMethod`
* [#1143](https://github.com/stripe/stripe-php/pull/1143) Streaming requests
* [#1138](https://github.com/stripe/stripe-php/pull/1138) Deprecate travis
## 7.86.0 - 2021-06-25
* [#1145](https://github.com/stripe/stripe-php/pull/1145) API Updates
* Add support for `boleto` on `PaymentMethod`.
* Add support for `il_vat` as a member of the `TaxID.Type` enum.
## 7.85.0 - 2021-06-18
* [#1142](https://github.com/stripe/stripe-php/pull/1142) API Updates
* Add support for new TaxId types: `ca_pst_mb`, `ca_pst_bc`, `ca_gst_hst`, and `ca_pst_sk`.
## 7.84.0 - 2021-06-16
* [#1141](https://github.com/stripe/stripe-php/pull/1141) Update PHPDocs
* Add support for `url` on `Checkout\Session`
## 7.83.0 - 2021-06-07
* [#1140](https://github.com/stripe/stripe-php/pull/1140) API Updates
* Added support for `tax_id_collection` on `Checkout\Session` and `Checkout\Session#create`
* Update `Location` to be expandable on `Terminal\Reader`
## 7.82.0 - 2021-06-04
* [#1136](https://github.com/stripe/stripe-php/pull/1136) Update PHPDocs
* Add support for `controller` on `Account`.
## 7.81.0 - 2021-06-04
* [#1135](https://github.com/stripe/stripe-php/pull/1135) API Updates
* Add support for new resource `TaxCode`
* Add support for `automatic_tax` `Invoice` and`Checkout.Session`.
* Add support for `tax_behavior` on `Price`
* Add support for `tax_code` on `Product`
* Add support for `tax` on `Customer`
* Add support for `tax_type` enum on `TaxRate`
## 7.80.0 - 2021-05-26
* [#1130](https://github.com/stripe/stripe-php/pull/1130) Update PHPDocs
## 7.79.0 - 2021-05-19
* [#1126](https://github.com/stripe/stripe-php/pull/1126) API Updates
* Added support for new resource `Identity.VerificationReport`
* Added support for new resource `Identity.VerificationSession`
* `File#list.purpose` and `File.purpose` added new enum members: `identity_document_downloadable` and `selfie`.
## 7.78.0 - 2021-05-05
* [#1120](https://github.com/stripe/stripe-php/pull/1120) Update PHPDocs
* Add support for `Radar.EarlyFraudWarning.payment_intent`
## 7.77.0 - 2021-04-12
* [#1110](https://github.com/stripe/stripe-php/pull/1110) Update PHPDocs
* Add support for `acss_debit` on `PaymentMethod`
* Add support for `payment_method_options` on `Checkout\Session`
* [#1107](https://github.com/stripe/stripe-php/pull/1107) Remove duplicate object phpdoc
## 7.76.0 - 2021-03-22
* [#1100](https://github.com/stripe/stripe-php/pull/1100) Update PHPDocs
* Added support for `amount_shipping` on `Checkout.Session.total_details`
* [#1088](https://github.com/stripe/stripe-php/pull/1088) Make possibility to extend CurlClient
## 7.75.0 - 2021-02-22
* [#1094](https://github.com/stripe/stripe-php/pull/1094) Add support for Billing Portal Configuration API
## 7.74.0 - 2021-02-17
* [#1093](https://github.com/stripe/stripe-php/pull/1093) Update PHPDocs
* Add support for on_behalf_of to Invoice
## 7.73.0 - 2021-02-16
* [#1091](https://github.com/stripe/stripe-php/pull/1091) Update PHPDocs
* Add support for `afterpay_clearpay` on `PaymentMethod`.
## 7.72.0 - 2021-02-08
* [#1089](https://github.com/stripe/stripe-php/pull/1089) Update PHPDocs
* Add support for `afterpay_clearpay_payments` on `Account.capabilities`
* Add support for `payment_settings` on `Invoice`
## 7.71.0 - 2021-02-05
* [#1087](https://github.com/stripe/stripe-php/pull/1087) Update PHPDocs
* [#1086](https://github.com/stripe/stripe-php/pull/1086) Update CA cert bundle URL
## 7.70.0 - 2021-02-03
* [#1085](https://github.com/stripe/stripe-php/pull/1085) Update PHPDocs
* Add support for `nationality` on `Person`
* Add member `gb_vat` of `TaxID` enum
## 7.69.0 - 2021-01-21
* [#1079](https://github.com/stripe/stripe-php/pull/1079) Update PHPDocs
## 7.68.0 - 2021-01-14
* [#1063](https://github.com/stripe/stripe-php/pull/1063) Multiple API changes
* [#1061](https://github.com/stripe/stripe-php/pull/1061) Bump phpDocumentor to 3.0.0
## 7.67.0 - 2020-12-09
* [#1060](https://github.com/stripe/stripe-php/pull/1060) Improve PHPDocs for `Discount`
* [#1059](https://github.com/stripe/stripe-php/pull/1059) Upgrade PHPStan to 0.12.59
* [#1057](https://github.com/stripe/stripe-php/pull/1057) Bump PHP-CS-Fixer and update code
## 7.66.1 - 2020-12-01
* [#1054](https://github.com/stripe/stripe-php/pull/1054) Improve error message for invalid keys in StripeClient
## 7.66.0 - 2020-11-24
* [#1053](https://github.com/stripe/stripe-php/pull/1053) Update PHPDocs
## 7.65.0 - 2020-11-19
* [#1050](https://github.com/stripe/stripe-php/pull/1050) Added constants for `proration_behavior` on `Subscription`
## 7.64.0 - 2020-11-18
* [#1049](https://github.com/stripe/stripe-php/pull/1049) Update PHPDocs
## 7.63.0 - 2020-11-17
* [#1048](https://github.com/stripe/stripe-php/pull/1048) Update PHPDocs
* [#1046](https://github.com/stripe/stripe-php/pull/1046) Force IPv4 resolving
## 7.62.0 - 2020-11-09
* [#1041](https://github.com/stripe/stripe-php/pull/1041) Add missing constants on `Event`
* [#1038](https://github.com/stripe/stripe-php/pull/1038) Update PHPDocs
## 7.61.0 - 2020-10-20
* [#1030](https://github.com/stripe/stripe-php/pull/1030) Add support for `jp_rn` and `ru_kpp` as a `type` on `TaxId`
## 7.60.0 - 2020-10-15
* [#1027](https://github.com/stripe/stripe-php/pull/1027) Warn if opts are in params
## 7.58.0 - 2020-10-14
* [#1026](https://github.com/stripe/stripe-php/pull/1026) Add support for the Payout Reverse API
## 7.57.0 - 2020-09-29
* [#1020](https://github.com/stripe/stripe-php/pull/1020) Add support for the `SetupAttempt` resource and List API
## 7.56.0 - 2020-09-25
* [#1019](https://github.com/stripe/stripe-php/pull/1019) Update PHPDocs
## 7.55.0 - 2020-09-24
* [#1018](https://github.com/stripe/stripe-php/pull/1018) Multiple API changes
* Updated PHPDocs
* Added `TYPE_CONTRIBUTION` as a constant on `BalanceTransaction`
## 7.54.0 - 2020-09-23
* [#1017](https://github.com/stripe/stripe-php/pull/1017) Updated PHPDoc
## 7.53.1 - 2020-09-22
* [#1015](https://github.com/stripe/stripe-php/pull/1015) Bugfix: don't error on systems with php_uname in disablefunctions with whitespace
## 7.53.0 - 2020-09-21
* [#1016](https://github.com/stripe/stripe-php/pull/1016) Updated PHPDocs
## 7.52.0 - 2020-09-08
* [#1010](https://github.com/stripe/stripe-php/pull/1010) Update PHPDocs
## 7.51.0 - 2020-09-02
* [#1007](https://github.com/stripe/stripe-php/pull/1007) Multiple API changes
* Add support for the Issuing Dispute Submit API
* Add constants for `payment_status` on Checkout `Session`
* [#1003](https://github.com/stripe/stripe-php/pull/1003) Add trim to getSignatures to allow for leading whitespace.
## 7.50.0 - 2020-08-28
* [#1005](https://github.com/stripe/stripe-php/pull/1005) Updated PHPDocs
## 7.49.0 - 2020-08-19
* [#998](https://github.com/stripe/stripe-php/pull/998) PHPDocs updated
## 7.48.0 - 2020-08-17
* [#997](https://github.com/stripe/stripe-php/pull/997) PHPDocs updated
* [#996](https://github.com/stripe/stripe-php/pull/996) Fixing telemetry
## 7.47.0 - 2020-08-13
* [#994](https://github.com/stripe/stripe-php/pull/994) Nullable balance_transactions on issuing disputes
* [#991](https://github.com/stripe/stripe-php/pull/991) Fix invalid return types in OAuthService

View File

@ -1,13 +1,8 @@
export PHPDOCUMENTOR_VERSION := v3.0.0-rc
export PHPSTAN_VERSION := 0.12.18
export PHPDOCUMENTOR_VERSION := v3.0.0
vendor: composer.json
composer install
vendor/bin/phpstan: vendor
curl -sfL https://github.com/phpstan/phpstan/releases/download/$(PHPSTAN_VERSION)/phpstan.phar -o vendor/bin/phpstan
chmod +x vendor/bin/phpstan
vendor/bin/phpdoc: vendor
curl -sfL https://github.com/phpDocumentor/phpDocumentor/releases/download/$(PHPDOCUMENTOR_VERSION)/phpDocumentor.phar -o vendor/bin/phpdoc
chmod +x vendor/bin/phpdoc
@ -27,10 +22,18 @@ fmtcheck: vendor
phpdoc: vendor/bin/phpdoc
vendor/bin/phpdoc
phpstan: vendor/bin/phpstan
phpstan: vendor
php -d memory_limit=512M vendor/bin/phpstan analyse lib tests
.PHONY: phpstan
phpstan-baseline: vendor/bin/phpstan
php -d memory_limit=512M vendor/bin/phpstan analyse lib tests --generate-baseline
.PHONY: phpstan-baseline
update-version:
@echo "$(VERSION)" > VERSION
@perl -pi -e 's|VERSION = '\''[.\d]+'\''|VERSION = '\''$(VERSION)'\''|' lib/Stripe.php
.PHONY: update-version
codegen-format: fmt
.PHONY: codegen-format

View File

@ -4,7 +4,6 @@
[![Latest Stable Version](https://poser.pugx.org/stripe/stripe-php/v/stable.svg)](https://packagist.org/packages/stripe/stripe-php)
[![Total Downloads](https://poser.pugx.org/stripe/stripe-php/downloads.svg)](https://packagist.org/packages/stripe/stripe-php)
[![License](https://poser.pugx.org/stripe/stripe-php/license.svg)](https://packagist.org/packages/stripe/stripe-php)
[![Code Coverage](https://coveralls.io/repos/stripe/stripe-php/badge.svg?branch=master)](https://coveralls.io/r/stripe/stripe-php?branch=master)
The Stripe PHP library provides convenient access to the Stripe API from
applications written in the PHP language. It includes a pre-defined set of
@ -68,21 +67,25 @@ You can continue to use the legacy integration patterns used prior to version [7
## Documentation
See the [PHP API docs](https://stripe.com/docs/api/php#intro).
See the [PHP API docs](https://stripe.com/docs/api/?lang=php#intro).
See [video demonstrations][youtube-playlist] covering how to use the library.
## Legacy Version Support
### PHP 5.4 & 5.5
If you are using PHP 5.4 or 5.5, you can download v6.21.1 ([zip](https://github.com/stripe/stripe-php/archive/v6.21.1.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.9.2.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
If you are using PHP 5.4 or 5.5, you should consider upgrading your environment as those versions have been past end of life since September 2015 and July 2016 respectively.
Otherwise, you can still use Stripe by downloading stripe-php v6.43.1 ([zip](https://github.com/stripe/stripe-php/archive/v6.43.1.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/6.43.1.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will work but might not support recent features we added since the version was released and upgrading PHP is the best course of action.
### PHP 5.3
If you are using PHP 5.3, you can download v5.9.2 ([zip](https://github.com/stripe/stripe-php/archive/v5.9.2.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.9.2.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
If you are using PHP 5.3, you should upgrade your environment as this version has been past end of life since August 2014.
Otherwise, you can download v5.9.2 ([zip](https://github.com/stripe/stripe-php/archive/v5.9.2.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.9.2.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
## Custom Request Timeouts
_NOTE:_ We do not recommend decreasing the timeout for non-read-only calls (e.g. charge creation), since even if you locally timeout, the request on Stripe's side can still complete. If you are decreasing timeouts on these calls, make sure to use [idempotency tokens](https://stripe.com/docs/api/php#idempotent_requests) to avoid executing the same transaction twice as a result of timeout retry logic.
_NOTE:_ We do not recommend decreasing the timeout for non-read-only calls (e.g. charge creation), since even if you locally timeout, the request on Stripe's side can still complete. If you are decreasing timeouts on these calls, make sure to use [idempotency tokens](https://stripe.com/docs/api/?lang=php#idempotent_requests) to avoid executing the same transaction twice as a result of timeout retry logic.
To modify request timeouts (connect or total, in seconds) you'll need to tell the API client to use a CurlClient other than its default. You'll set the timeouts in that CurlClient.
@ -228,7 +231,7 @@ Install dependencies as mentioned above (which will resolve [PHPUnit](http://pac
Or to run an individual test file:
```bash
./vendor/bin/phpunit tests/UtilTest.php
./vendor/bin/phpunit tests/Stripe/UtilTest.php
```
Update bundled CA certificates from the [Mozilla cURL release][curl]:
@ -260,7 +263,8 @@ See the "SSL / TLS compatibility issues" paragraph above for full context. If yo
[composer]: https://getcomposer.org/
[connect]: https://stripe.com/connect
[curl]: http://curl.haxx.se/docs/caextract.html
[idempotency-keys]: https://stripe.com/docs/api/php#idempotent_requests
[idempotency-keys]: https://stripe.com/docs/api/?lang=php#idempotent_requests
[php-cs-fixer]: https://github.com/FriendsOfPHP/PHP-CS-Fixer
[psr3]: http://www.php-fig.org/psr/psr-3/
[stripe-mock]: https://github.com/stripe/stripe-mock
[youtube-playlist]: https://www.youtube.com/playlist?list=PLy1nL-pvL2M6cUbiHrfMkXxZ9j9SGBxFE

View File

@ -21,11 +21,10 @@
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5.7 || ^9.0",
"squizlabs/php_codesniffer": "^3.3",
"symfony/process": "~3.4",
"friendsofphp/php-cs-fixer": "2.16.1"
"friendsofphp/php-cs-fixer": "3.5.0",
"phpstan/phpstan": "^1.2"
},
"autoload": {
"psr-4": {

View File

@ -17,6 +17,7 @@ require __DIR__ . '/lib/Util/ObjectTypes.php';
// HttpClient
require __DIR__ . '/lib/HttpClient/ClientInterface.php';
require __DIR__ . '/lib/HttpClient/StreamingClientInterface.php';
require __DIR__ . '/lib/HttpClient/CurlClient.php';
// Exceptions
@ -53,6 +54,7 @@ require __DIR__ . '/lib/ApiOperations/Delete.php';
require __DIR__ . '/lib/ApiOperations/NestedResource.php';
require __DIR__ . '/lib/ApiOperations/Request.php';
require __DIR__ . '/lib/ApiOperations/Retrieve.php';
require __DIR__ . '/lib/ApiOperations/Search.php';
require __DIR__ . '/lib/ApiOperations/Update.php';
// Plumbing
@ -66,7 +68,9 @@ require __DIR__ . '/lib/Service/AbstractService.php';
require __DIR__ . '/lib/Service/AbstractServiceFactory.php';
// StripeClient
require __DIR__ . '/lib/BaseStripeClientInterface.php';
require __DIR__ . '/lib/StripeClientInterface.php';
require __DIR__ . '/lib/StripeStreamingClientInterface.php';
require __DIR__ . '/lib/BaseStripeClient.php';
require __DIR__ . '/lib/StripeClient.php';
@ -80,11 +84,13 @@ require __DIR__ . '/lib/ApplicationFeeRefund.php';
require __DIR__ . '/lib/Balance.php';
require __DIR__ . '/lib/BalanceTransaction.php';
require __DIR__ . '/lib/BankAccount.php';
require __DIR__ . '/lib/BillingPortal/Configuration.php';
require __DIR__ . '/lib/BillingPortal/Session.php';
require __DIR__ . '/lib/BitcoinReceiver.php';
require __DIR__ . '/lib/BitcoinTransaction.php';
require __DIR__ . '/lib/Capability.php';
require __DIR__ . '/lib/Card.php';
require __DIR__ . '/lib/CashBalance.php';
require __DIR__ . '/lib/Charge.php';
require __DIR__ . '/lib/Checkout/Session.php';
require __DIR__ . '/lib/Collection.php';
@ -102,6 +108,13 @@ require __DIR__ . '/lib/Event.php';
require __DIR__ . '/lib/ExchangeRate.php';
require __DIR__ . '/lib/File.php';
require __DIR__ . '/lib/FileLink.php';
require __DIR__ . '/lib/FinancialConnections/Account.php';
require __DIR__ . '/lib/FinancialConnections/AccountOwner.php';
require __DIR__ . '/lib/FinancialConnections/AccountOwnership.php';
require __DIR__ . '/lib/FinancialConnections/Session.php';
require __DIR__ . '/lib/FundingInstructions.php';
require __DIR__ . '/lib/Identity/VerificationReport.php';
require __DIR__ . '/lib/Identity/VerificationSession.php';
require __DIR__ . '/lib/Invoice.php';
require __DIR__ . '/lib/InvoiceItem.php';
require __DIR__ . '/lib/InvoiceLineItem.php';
@ -118,6 +131,7 @@ require __DIR__ . '/lib/Order.php';
require __DIR__ . '/lib/OrderItem.php';
require __DIR__ . '/lib/OrderReturn.php';
require __DIR__ . '/lib/PaymentIntent.php';
require __DIR__ . '/lib/PaymentLink.php';
require __DIR__ . '/lib/PaymentMethod.php';
require __DIR__ . '/lib/Payout.php';
require __DIR__ . '/lib/Person.php';
@ -125,6 +139,7 @@ require __DIR__ . '/lib/Plan.php';
require __DIR__ . '/lib/Price.php';
require __DIR__ . '/lib/Product.php';
require __DIR__ . '/lib/PromotionCode.php';
require __DIR__ . '/lib/Quote.php';
require __DIR__ . '/lib/Radar/EarlyFraudWarning.php';
require __DIR__ . '/lib/Radar/ValueList.php';
require __DIR__ . '/lib/Radar/ValueListItem.php';
@ -134,7 +149,10 @@ require __DIR__ . '/lib/Refund.php';
require __DIR__ . '/lib/Reporting/ReportRun.php';
require __DIR__ . '/lib/Reporting/ReportType.php';
require __DIR__ . '/lib/Review.php';
require __DIR__ . '/lib/SearchResult.php';
require __DIR__ . '/lib/SetupAttempt.php';
require __DIR__ . '/lib/SetupIntent.php';
require __DIR__ . '/lib/ShippingRate.php';
require __DIR__ . '/lib/Sigma/ScheduledQueryRun.php';
require __DIR__ . '/lib/SKU.php';
require __DIR__ . '/lib/Source.php';
@ -142,11 +160,14 @@ require __DIR__ . '/lib/SourceTransaction.php';
require __DIR__ . '/lib/Subscription.php';
require __DIR__ . '/lib/SubscriptionItem.php';
require __DIR__ . '/lib/SubscriptionSchedule.php';
require __DIR__ . '/lib/TaxCode.php';
require __DIR__ . '/lib/TaxId.php';
require __DIR__ . '/lib/TaxRate.php';
require __DIR__ . '/lib/Terminal/Configuration.php';
require __DIR__ . '/lib/Terminal/ConnectionToken.php';
require __DIR__ . '/lib/Terminal/Location.php';
require __DIR__ . '/lib/Terminal/Reader.php';
require __DIR__ . '/lib/TestHelpers/TestClock.php';
require __DIR__ . '/lib/ThreeDSecure.php';
require __DIR__ . '/lib/Token.php';
require __DIR__ . '/lib/Topup.php';
@ -163,6 +184,7 @@ require __DIR__ . '/lib/Service/ApplePayDomainService.php';
require __DIR__ . '/lib/Service/ApplicationFeeService.php';
require __DIR__ . '/lib/Service/BalanceService.php';
require __DIR__ . '/lib/Service/BalanceTransactionService.php';
require __DIR__ . '/lib/Service/BillingPortal/ConfigurationService.php';
require __DIR__ . '/lib/Service/BillingPortal/SessionService.php';
require __DIR__ . '/lib/Service/ChargeService.php';
require __DIR__ . '/lib/Service/Checkout/SessionService.php';
@ -176,6 +198,10 @@ require __DIR__ . '/lib/Service/EventService.php';
require __DIR__ . '/lib/Service/ExchangeRateService.php';
require __DIR__ . '/lib/Service/FileService.php';
require __DIR__ . '/lib/Service/FileLinkService.php';
require __DIR__ . '/lib/Service/FinancialConnections/AccountService.php';
require __DIR__ . '/lib/Service/FinancialConnections/SessionService.php';
require __DIR__ . '/lib/Service/Identity/VerificationReportService.php';
require __DIR__ . '/lib/Service/Identity/VerificationSessionService.php';
require __DIR__ . '/lib/Service/InvoiceService.php';
require __DIR__ . '/lib/Service/InvoiceItemService.php';
require __DIR__ . '/lib/Service/Issuing/AuthorizationService.php';
@ -187,12 +213,14 @@ require __DIR__ . '/lib/Service/MandateService.php';
require __DIR__ . '/lib/Service/OrderService.php';
require __DIR__ . '/lib/Service/OrderReturnService.php';
require __DIR__ . '/lib/Service/PaymentIntentService.php';
require __DIR__ . '/lib/Service/PaymentLinkService.php';
require __DIR__ . '/lib/Service/PaymentMethodService.php';
require __DIR__ . '/lib/Service/PayoutService.php';
require __DIR__ . '/lib/Service/PlanService.php';
require __DIR__ . '/lib/Service/PriceService.php';
require __DIR__ . '/lib/Service/ProductService.php';
require __DIR__ . '/lib/Service/PromotionCodeService.php';
require __DIR__ . '/lib/Service/QuoteService.php';
require __DIR__ . '/lib/Service/Radar/EarlyFraudWarningService.php';
require __DIR__ . '/lib/Service/Radar/ValueListService.php';
require __DIR__ . '/lib/Service/Radar/ValueListItemService.php';
@ -200,31 +228,41 @@ require __DIR__ . '/lib/Service/RefundService.php';
require __DIR__ . '/lib/Service/Reporting/ReportRunService.php';
require __DIR__ . '/lib/Service/Reporting/ReportTypeService.php';
require __DIR__ . '/lib/Service/ReviewService.php';
require __DIR__ . '/lib/Service/SetupAttemptService.php';
require __DIR__ . '/lib/Service/SetupIntentService.php';
require __DIR__ . '/lib/Service/ShippingRateService.php';
require __DIR__ . '/lib/Service/Sigma/ScheduledQueryRunService.php';
require __DIR__ . '/lib/Service/SkuService.php';
require __DIR__ . '/lib/Service/SourceService.php';
require __DIR__ . '/lib/Service/SubscriptionService.php';
require __DIR__ . '/lib/Service/SubscriptionItemService.php';
require __DIR__ . '/lib/Service/SubscriptionScheduleService.php';
require __DIR__ . '/lib/Service/TaxCodeService.php';
require __DIR__ . '/lib/Service/TaxRateService.php';
require __DIR__ . '/lib/Service/Terminal/ConfigurationService.php';
require __DIR__ . '/lib/Service/Terminal/ConnectionTokenService.php';
require __DIR__ . '/lib/Service/Terminal/LocationService.php';
require __DIR__ . '/lib/Service/Terminal/ReaderService.php';
require __DIR__ . '/lib/Service/TestHelpers/RefundService.php';
require __DIR__ . '/lib/Service/TestHelpers/Terminal/ReaderService.php';
require __DIR__ . '/lib/Service/TestHelpers/TestClockService.php';
require __DIR__ . '/lib/Service/TokenService.php';
require __DIR__ . '/lib/Service/TopupService.php';
require __DIR__ . '/lib/Service/TransferService.php';
require __DIR__ . '/lib/Service/WebhookEndpointService.php';
// Service factories
require __DIR__ . '/lib/Service/CoreServiceFactory.php';
require __DIR__ . '/lib/Service/BillingPortal/BillingPortalServiceFactory.php';
require __DIR__ . '/lib/Service/Checkout/CheckoutServiceFactory.php';
require __DIR__ . '/lib/Service/CoreServiceFactory.php';
require __DIR__ . '/lib/Service/FinancialConnections/FinancialConnectionsServiceFactory.php';
require __DIR__ . '/lib/Service/Identity/IdentityServiceFactory.php';
require __DIR__ . '/lib/Service/Issuing/IssuingServiceFactory.php';
require __DIR__ . '/lib/Service/Radar/RadarServiceFactory.php';
require __DIR__ . '/lib/Service/Reporting/ReportingServiceFactory.php';
require __DIR__ . '/lib/Service/Sigma/SigmaServiceFactory.php';
require __DIR__ . '/lib/Service/Terminal/TerminalServiceFactory.php';
require __DIR__ . '/lib/Service/TestHelpers/TestHelpersServiceFactory.php';
// OAuth
require __DIR__ . '/lib/OAuth.php';

View File

@ -20,13 +20,15 @@ namespace Stripe;
* @property \Stripe\StripeObject $capabilities
* @property bool $charges_enabled Whether the account can create live charges.
* @property \Stripe\StripeObject $company
* @property \Stripe\StripeObject $controller
* @property string $country The account's country.
* @property int $created Time at which the object was created. Measured in seconds since the Unix epoch.
* @property int $created Time at which the account was connected. Measured in seconds since the Unix epoch.
* @property string $default_currency Three-letter ISO currency code representing the default currency for the account. This must be a currency that <a href="https://stripe.com/docs/payouts">Stripe supports in the account's country</a>.
* @property bool $details_submitted Whether account details have been submitted. Standard accounts cannot receive payouts before this is true.
* @property null|string $email The primary user's email address.
* @property \Stripe\Collection $external_accounts External accounts (bank accounts and debit cards) currently attached to this account
* @property \Stripe\Person $individual <p>This is an object representing a person associated with a Stripe account.</p><p>Related guide: <a href="https://stripe.com/docs/connect/identity-verification-api#person-information">Handling Identity Verification with the API</a>.</p>
* @property null|string $email An email address associated with the account. You can treat this as metadata: it is not used for authentication or messaging account holders.
* @property \Stripe\Collection<\Stripe\BankAccount|\Stripe\Card> $external_accounts External accounts (bank accounts and debit cards) currently attached to this account
* @property \Stripe\StripeObject $future_requirements
* @property \Stripe\Person $individual <p>This is an object representing a person associated with a Stripe account.</p><p>A platform cannot access a Standard or Express account's persons after the account starts onboarding, such as after generating an account link for the account. See the <a href="https://stripe.com/docs/connect/standard-accounts">Standard onboarding</a> or <a href="https://stripe.com/docs/connect/express-accounts">Express onboarding documentation</a> for information about platform pre-filling and account onboarding steps.</p><p>Related guide: <a href="https://stripe.com/docs/connect/identity-verification-api#person-information">Handling Identity Verification with the API</a>.</p>
* @property \Stripe\StripeObject $metadata Set of <a href="https://stripe.com/docs/api/metadata">key-value pairs</a> that you can attach to an object. This can be useful for storing additional information about the object in a structured format.
* @property bool $payouts_enabled Whether Stripe can send payouts to this account.
* @property \Stripe\StripeObject $requirements
@ -127,10 +129,10 @@ class Account extends ApiResource
foreach ($additionalOwners as $i => $v) {
$update = ($v instanceof StripeObject) ? $v->serializeParameters() : $v;
if ($update !== []) {
if (!$originalValue ||
!\array_key_exists($i, $originalValue) ||
($update !== $legalEntity->serializeParamsValue($originalValue[$i], null, false, true))) {
if ([] !== $update) {
if (!$originalValue
|| !\array_key_exists($i, $originalValue)
|| ($update !== $legalEntity->serializeParamsValue($originalValue[$i], null, false, true))) {
$updateArr[$i] = $update;
}
}
@ -182,7 +184,7 @@ class Account extends ApiResource
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*
* @return \Stripe\Collection the list of persons
* @return \Stripe\Collection<\Stripe\Person> the list of persons
*/
public function persons($params = null, $opts = null)
{
@ -200,7 +202,7 @@ class Account extends ApiResource
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*
* @return Account the rejected account
* @return \Stripe\Account the rejected account
*/
public function reject($params = null, $opts = null)
{
@ -226,7 +228,7 @@ class Account extends ApiResource
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*
* @return \Stripe\Collection the list of capabilities
* @return \Stripe\Collection<\Stripe\Capability> the list of capabilities
*/
public static function allCapabilities($id, $params = null, $opts = null)
{
@ -262,7 +264,6 @@ class Account extends ApiResource
{
return self::_updateNestedResource($id, static::PATH_CAPABILITIES, $capabilityId, $params, $opts);
}
const PATH_EXTERNAL_ACCOUNTS = '/external_accounts';
/**
@ -272,7 +273,7 @@ class Account extends ApiResource
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*
* @return \Stripe\Collection the list of external accounts (BankAccount or Card)
* @return \Stripe\Collection<\Stripe\BankAccount|\Stripe\Card> the list of external accounts (BankAccount or Card)
*/
public static function allExternalAccounts($id, $params = null, $opts = null)
{
@ -337,7 +338,6 @@ class Account extends ApiResource
{
return self::_updateNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
}
const PATH_LOGIN_LINKS = '/login_links';
/**
@ -353,7 +353,6 @@ class Account extends ApiResource
{
return self::_createNestedResource($id, static::PATH_LOGIN_LINKS, $params, $opts);
}
const PATH_PERSONS = '/persons';
/**
@ -363,7 +362,7 @@ class Account extends ApiResource
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*
* @return \Stripe\Collection the list of persons
* @return \Stripe\Collection<\Stripe\Person> the list of persons
*/
public static function allPersons($id, $params = null, $opts = null)
{

View File

@ -45,6 +45,21 @@ trait Request
return [$resp->json, $options];
}
/**
* @param string $method HTTP method ('get', 'post', etc.)
* @param string $url URL for the request
* @param callable $readBodyChunk function that will receive chunks of data from a successful request body
* @param array $params list of parameters for the request
* @param null|array|string $options
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*/
protected function _requestStream($method, $url, $readBodyChunk, $params = [], $options = null)
{
$opts = $this->_opts->merge($options);
static::_staticStreamingRequest($method, $url, $readBodyChunk, $params, $opts);
}
/**
* @param string $method HTTP method ('get', 'post', etc.)
* @param string $url URL for the request
@ -65,4 +80,21 @@ trait Request
return [$response, $opts];
}
/**
* @param string $method HTTP method ('get', 'post', etc.)
* @param string $url URL for the request
* @param callable $readBodyChunk function that will receive chunks of data from a successful request body
* @param array $params list of parameters for the request
* @param null|array|string $options
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*/
protected static function _staticStreamingRequest($method, $url, $readBodyChunk, $params, $options)
{
$opts = \Stripe\Util\RequestOptions::parse($options);
$baseUrl = isset($opts->apiBase) ? $opts->apiBase : static::baseUrl();
$requestor = new \Stripe\ApiRequestor($opts->apiKey, $baseUrl);
$requestor->requestStream($method, $url, $readBodyChunk, $params, $opts->headers);
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Stripe\ApiOperations;
/**
* Trait for searchable resources.
*
* This trait should only be applied to classes that derive from StripeObject.
*/
trait Search
{
/**
* @param string $searchUrl
* @param null|array $params
* @param null|array|string $opts
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*
* @return \Stripe\SearchResult of ApiResources
*/
protected static function _searchResource($searchUrl, $params = null, $opts = null)
{
self::_validateParams($params);
list($response, $opts) = static::_staticRequest('get', $searchUrl, $params, $opts);
$obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
if (!($obj instanceof \Stripe\SearchResult)) {
throw new \Stripe\Exception\UnexpectedValueException(
'Expected type ' . \Stripe\SearchResult::class . ', got "' . \get_class($obj) . '" instead.'
);
}
$obj->setLastResponse($response);
$obj->setFilters($params);
return $obj;
}
}

View File

@ -21,12 +21,18 @@ class ApiRequestor
* @var HttpClient\ClientInterface
*/
private static $_httpClient;
/**
* @var HttpClient\StreamingClientInterface
*/
private static $_streamingHttpClient;
/**
* @var RequestTelemetry
*/
private static $requestTelemetry;
private static $OPTIONS_KEYS = ['api_key', 'idempotency_key', 'stripe_account', 'stripe_version', 'api_base'];
/**
* ApiRequestor constructor.
*
@ -121,6 +127,26 @@ class ApiRequestor
return [$resp, $myApiKey];
}
/**
* @param string $method
* @param string $url
* @param callable $readBodyChunkCallable
* @param null|array $params
* @param null|array $headers
*
* @throws Exception\ApiErrorException
*/
public function requestStream($method, $url, $readBodyChunkCallable, $params = null, $headers = null)
{
$params = $params ?: [];
$headers = $headers ?: [];
list($rbody, $rcode, $rheaders, $myApiKey) =
$this->_requestRawStreaming($method, $url, $params, $headers, $readBodyChunkCallable);
if ($rcode >= 300) {
$this->_interpretResponse($rbody, $rcode, $rheaders);
}
}
/**
* @param string $rbody a JSON string
* @param int $rcode
@ -185,14 +211,19 @@ class ApiRequestor
// no break
case 404:
return Exception\InvalidRequestException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code, $param);
case 401:
return Exception\AuthenticationException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code);
case 402:
return Exception\CardException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code, $declineCode, $param);
case 403:
return Exception\PermissionException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code);
case 429:
return Exception\RateLimitException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code, $param);
default:
return Exception\UnknownApiErrorException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code);
}
@ -216,16 +247,22 @@ class ApiRequestor
switch ($errorCode) {
case 'invalid_client':
return Exception\OAuth\InvalidClientException::factory($description, $rcode, $rbody, $resp, $rheaders, $errorCode);
case 'invalid_grant':
return Exception\OAuth\InvalidGrantException::factory($description, $rcode, $rbody, $resp, $rheaders, $errorCode);
case 'invalid_request':
return Exception\OAuth\InvalidRequestException::factory($description, $rcode, $rbody, $resp, $rheaders, $errorCode);
case 'invalid_scope':
return Exception\OAuth\InvalidScopeException::factory($description, $rcode, $rbody, $resp, $rheaders, $errorCode);
case 'unsupported_grant_type':
return Exception\OAuth\UnsupportedGrantTypeException::factory($description, $rcode, $rbody, $resp, $rheaders, $errorCode);
case 'unsupported_response_type':
return Exception\OAuth\UnsupportedResponseTypeException::factory($description, $rcode, $rbody, $resp, $rheaders, $errorCode);
default:
return Exception\OAuth\UnknownOAuthErrorException::factory($description, $rcode, $rbody, $resp, $rheaders, $errorCode);
}
@ -255,6 +292,27 @@ class ApiRequestor
return null;
}
/**
* @static
*
* @param string $disabledFunctionsOutput - String value of the 'disable_function' setting, as output by \ini_get('disable_functions')
* @param string $functionName - Name of the function we are interesting in seeing whether or not it is disabled
* @param mixed $disableFunctionsOutput
*
* @return bool
*/
private static function _isDisabled($disableFunctionsOutput, $functionName)
{
$disabledFunctions = \explode(',', $disableFunctionsOutput);
foreach ($disabledFunctions as $disabledFunction) {
if (\trim($disabledFunction) === $functionName) {
return true;
}
}
return false;
}
/**
* @static
*
@ -268,7 +326,7 @@ class ApiRequestor
$uaString = 'Stripe/v1 PhpBindings/' . Stripe::VERSION;
$langVersion = \PHP_VERSION;
$uname_disabled = \in_array('php_uname', \explode(',', \ini_get('disable_functions')), true);
$uname_disabled = static::_isDisabled(\ini_get('disable_functions'), 'php_uname');
$uname = $uname_disabled ? '(disabled)' : \php_uname();
$appInfo = Stripe::getAppInfo();
@ -294,18 +352,7 @@ class ApiRequestor
];
}
/**
* @param string $method
* @param string $url
* @param array $params
* @param array $headers
*
* @throws Exception\AuthenticationException
* @throws Exception\ApiConnectionException
*
* @return array
*/
private function _requestRaw($method, $url, $params, $headers)
private function _prepareRequest($method, $url, $params, $headers)
{
$myApiKey = $this->_apiKey;
if (!$myApiKey) {
@ -329,6 +376,21 @@ class ApiRequestor
$clientUAInfo = $this->httpClient()->getUserAgentInfo();
}
if ($params && \is_array($params)) {
$optionKeysInParams = \array_filter(
static::$OPTIONS_KEYS,
function ($key) use ($params) {
return \array_key_exists($key, $params);
}
);
if (\count($optionKeysInParams) > 0) {
$message = \sprintf('Options found in $params: %s. Options should '
. 'be passed in their own array after $params. (HINT: pass an '
. 'empty array to $params if you do not have any.)', \implode(', ', $optionKeysInParams));
\trigger_error($message, \E_USER_WARNING);
}
}
$absUrl = $this->_apiBase . $url;
$params = self::_encodeObjects($params);
$defaultHeaders = $this->_defaultHeaders($myApiKey, $clientUAInfo);
@ -367,6 +429,24 @@ class ApiRequestor
$rawHeaders[] = $header . ': ' . $value;
}
return [$absUrl, $rawHeaders, $params, $hasFile, $myApiKey];
}
/**
* @param string $method
* @param string $url
* @param array $params
* @param array $headers
*
* @throws Exception\AuthenticationException
* @throws Exception\ApiConnectionException
*
* @return array
*/
private function _requestRaw($method, $url, $params, $headers)
{
list($absUrl, $rawHeaders, $params, $hasFile, $myApiKey) = $this->_prepareRequest($method, $url, $params, $headers);
$requestStartMs = Util\Util::currentTimeMillis();
list($rbody, $rcode, $rheaders) = $this->httpClient()->request(
@ -377,9 +457,51 @@ class ApiRequestor
$hasFile
);
if (isset($rheaders['request-id'], $rheaders['request-id'][0])) {
if (isset($rheaders['request-id'])
&& \is_string($rheaders['request-id'])
&& '' !== $rheaders['request-id']) {
self::$requestTelemetry = new RequestTelemetry(
$rheaders['request-id'][0],
$rheaders['request-id'],
Util\Util::currentTimeMillis() - $requestStartMs
);
}
return [$rbody, $rcode, $rheaders, $myApiKey];
}
/**
* @param string $method
* @param string $url
* @param array $params
* @param array $headers
* @param callable $readBodyChunk
* @param mixed $readBodyChunkCallable
*
* @throws Exception\AuthenticationException
* @throws Exception\ApiConnectionException
*
* @return array
*/
private function _requestRawStreaming($method, $url, $params, $headers, $readBodyChunkCallable)
{
list($absUrl, $rawHeaders, $params, $hasFile, $myApiKey) = $this->_prepareRequest($method, $url, $params, $headers);
$requestStartMs = Util\Util::currentTimeMillis();
list($rbody, $rcode, $rheaders) = $this->streamingHttpClient()->requestStream(
$method,
$absUrl,
$rawHeaders,
$params,
$hasFile,
$readBodyChunkCallable
);
if (isset($rheaders['request-id'])
&& \is_string($rheaders['request-id'])
&& '' !== $rheaders['request-id']) {
self::$requestTelemetry = new RequestTelemetry(
$rheaders['request-id'],
Util\Util::currentTimeMillis() - $requestStartMs
);
}
@ -451,6 +573,16 @@ class ApiRequestor
self::$_httpClient = $client;
}
/**
* @static
*
* @param HttpClient\StreamingClientInterface $client
*/
public static function setStreamingHttpClient($client)
{
self::$_streamingHttpClient = $client;
}
/**
* @static
*
@ -472,4 +604,16 @@ class ApiRequestor
return self::$_httpClient;
}
/**
* @return HttpClient\StreamingClientInterface
*/
private function streamingHttpClient()
{
if (!self::$_streamingHttpClient) {
self::$_streamingHttpClient = HttpClient\CurlClient::instance();
}
return self::$_streamingHttpClient;
}
}

View File

@ -39,8 +39,8 @@ abstract class ApiResource extends StripeObject
{
parent::__set($k, $v);
$v = $this->{$k};
if ((static::getSavedNestedResources()->includes($k)) &&
($v instanceof ApiResource)) {
if ((static::getSavedNestedResources()->includes($k))
&& ($v instanceof ApiResource)) {
$v->saveWithParent = true;
}
}
@ -82,6 +82,8 @@ abstract class ApiResource extends StripeObject
{
// Replace dots with slashes for namespaced resources, e.g. if the object's name is
// "foo.bar", then its URL will be "/v1/foo/bars".
/** @phpstan-ignore-next-line */
$base = \str_replace('.', '/', static::OBJECT_NAME);
return "/v1/{$base}s";

View File

@ -18,7 +18,7 @@ namespace Stripe;
* @property bool $livemode Has the value <code>true</code> if the object exists in live mode or the value <code>false</code> if the object exists in test mode.
* @property null|string|\Stripe\Charge $originating_transaction ID of the corresponding charge on the platform account, if this fee was the result of a charge using the <code>destination</code> parameter.
* @property bool $refunded Whether the fee has been fully refunded. If the fee is only partially refunded, this attribute will still be false.
* @property \Stripe\Collection $refunds A list of refunds that have been applied to the fee.
* @property \Stripe\Collection<\Stripe\StripeObject> $refunds A list of refunds that have been applied to the fee.
*/
class ApplicationFee extends ApiResource
{
@ -37,7 +37,7 @@ class ApplicationFee extends ApiResource
*
* @throws \Stripe\Exception\ApiErrorException if the request fails
*
* @return \Stripe\Collection the list of fee refunds
* @return \Stripe\Collection<\Stripe\ApplicationFeeRefund> the list of fee refunds
*/
public static function allRefunds($id, $params = null, $opts = null)
{

View File

@ -20,7 +20,7 @@ namespace Stripe;
* @property int $created Time at which the object was created. Measured in seconds since the Unix epoch.
* @property string $currency Three-letter <a href="https://www.iso.org/iso-4217-currency-codes.html">ISO currency code</a>, in lowercase. Must be a <a href="https://stripe.com/docs/currencies">supported currency</a>.
* @property string|\Stripe\ApplicationFee $fee ID of the application fee that was refunded.
* @property \Stripe\StripeObject $metadata Set of <a href="https://stripe.com/docs/api/metadata">key-value pairs</a> that you can attach to an object. This can be useful for storing additional information about the object in a structured format.
* @property null|\Stripe\StripeObject $metadata Set of <a href="https://stripe.com/docs/api/metadata">key-value pairs</a> that you can attach to an object. This can be useful for storing additional information about the object in a structured format.
*/
class ApplicationFeeRefund extends ApiResource
{

View File

@ -22,6 +22,7 @@ namespace Stripe;
* @property string $object String representing the object's type. Objects of the same type share the same value.
* @property \Stripe\StripeObject[] $available Funds that are available to be transferred or paid out, whether automatically by Stripe or explicitly via the <a href="https://stripe.com/docs/api#transfers">Transfers API</a> or <a href="https://stripe.com/docs/api#payouts">Payouts API</a>. The available balance for each currency and payment type can be found in the <code>source_types</code> property.
* @property \Stripe\StripeObject[] $connect_reserved Funds held due to negative balances on connected Custom accounts. The connect reserve balance for each currency and payment type can be found in the <code>source_types</code> property.
* @property \Stripe\StripeObject[] $instant_available Funds that can be paid out using Instant Payouts.
* @property \Stripe\StripeObject $issuing
* @property bool $livemode Has the value <code>true</code> if the object exists in live mode or the value <code>false</code> if the object exists in test mode.
* @property \Stripe\StripeObject[] $pending Funds that are not yet available in the balance, due to the 7-day rolling pay cycle. The pending balance for each currency, and for each payment type, can be found in the <code>source_types</code> property.

View File

@ -27,7 +27,7 @@ namespace Stripe;
* @property string $reporting_category <a href="https://stripe.com/docs/reports/reporting-categories">Learn more</a> about how reporting categories can help you understand balance transactions from an accounting perspective.
* @property null|string|\Stripe\StripeObject $source The Stripe object to which this transaction is related.
* @property string $status If the transaction's net funds are available in the Stripe balance yet. Either <code>available</code> or <code>pending</code>.
* @property string $type Transaction type: <code>adjustment</code>, <code>advance</code>, <code>advance_funding</code>, <code>anticipation_repayment</code>, <code>application_fee</code>, <code>application_fee_refund</code>, <code>charge</code>, <code>connect_collection_transfer</code>, <code>issuing_authorization_hold</code>, <code>issuing_authorization_release</code>, <code>issuing_dispute</code>, <code>issuing_transaction</code>, <code>payment</code>, <code>payment_failure_refund</code>, <code>payment_refund</code>, <code>payout</code>, <code>payout_cancel</code>, <code>payout_failure</code>, <code>refund</code>, <code>refund_failure</code>, <code>reserve_transaction</code>, <code>reserved_funds</code>, <code>stripe_fee</code>, <code>stripe_fx_fee</code>, <code>tax_fee</code>, <code>topup</code>, <code>topup_reversal</code>, <code>transfer</code>, <code>transfer_cancel</code>, <code>transfer_failure</code>, or <code>transfer_refund</code>. <a href="https://stripe.com/docs/reports/balance-transaction-types">Learn more</a> about balance transaction types and what they represent. If you are looking to classify transactions for accounting purposes, you might want to consider <code>reporting_category</code> instead.
* @property string $type Transaction type: <code>adjustment</code>, <code>advance</code>, <code>advance_funding</code>, <code>anticipation_repayment</code>, <code>application_fee</code>, <code>application_fee_refund</code>, <code>charge</code>, <code>connect_collection_transfer</code>, <code>contribution</code>, <code>issuing_authorization_hold</code>, <code>issuing_authorization_release</code>, <code>issuing_dispute</code>, <code>issuing_transaction</code>, <code>payment</code>, <code>payment_failure_refund</code>, <code>payment_refund</code>, <code>payout</code>, <code>payout_cancel</code>, <code>payout_failure</code>, <code>refund</code>, <code>refund_failure</code>, <code>reserve_transaction</code>, <code>reserved_funds</code>, <code>stripe_fee</code>, <code>stripe_fx_fee</code>, <code>tax_fee</code>, <code>topup</code>, <code>topup_reversal</code>, <code>transfer</code>, <code>transfer_cancel</code>, <code>transfer_failure</code>, or <code>transfer_refund</code>. <a href="https://stripe.com/docs/reports/balance-transaction-types">Learn more</a> about balance transaction types and what they represent. If you are looking to classify transactions for accounting purposes, you might want to consider <code>reporting_category</code> instead.
*/
class BalanceTransaction extends ApiResource
{
@ -44,6 +44,7 @@ class BalanceTransaction extends ApiResource
const TYPE_APPLICATION_FEE_REFUND = 'application_fee_refund';
const TYPE_CHARGE = 'charge';
const TYPE_CONNECT_COLLECTION_TRANSFER = 'connect_collection_transfer';
const TYPE_CONTRIBUTION = 'contribution';
const TYPE_ISSUING_AUTHORIZATION_HOLD = 'issuing_authorization_hold';
const TYPE_ISSUING_AUTHORIZATION_RELEASE = 'issuing_authorization_release';
const TYPE_ISSUING_DISPUTE = 'issuing_dispute';

View File

@ -23,6 +23,8 @@ namespace Stripe;
* @property null|string|\Stripe\Account $account The ID of the account that the bank account is associated with.
* @property null|string $account_holder_name The name of the person or business that owns the bank account.
* @property null|string $account_holder_type The type of entity that holds the account. This can be either <code>individual</code> or <code>company</code>.
* @property null|string $account_type The bank account type. This can only be <code>checking</code> or <code>savings</code> in most countries. In Japan, this can only be <code>futsu</code> or <code>toza</code>.
* @property null|string[] $available_payout_methods A set of available payout methods for this bank account. Only values from this set should be passed as the <code>method</code> when creating a payout.
* @property null|string $bank_name Name of the bank associated with the routing number (e.g., <code>WELLS FARGO</code>).
* @property string $country Two-letter ISO code representing the country the bank account is located in.
* @property string $currency Three-letter <a href="https://stripe.com/docs/payouts">ISO code for the currency</a> paid out to the bank account.

View File

@ -2,7 +2,7 @@
namespace Stripe;
class BaseStripeClient implements StripeClientInterface
class BaseStripeClient implements StripeClientInterface, StripeStreamingClientInterface
{
/** @var string default base URL for Stripe's API */
const DEFAULT_API_BASE = 'https://api.stripe.com';
@ -139,6 +139,25 @@ class BaseStripeClient implements StripeClientInterface
return $obj;
}
/**
* Sends a request to Stripe's API, passing chunks of the streamed response
* into a user-provided $readBodyChunkCallable callback.
*
* @param string $method the HTTP method
* @param string $path the path of the request
* @param callable $readBodyChunkCallable a function that will be called
* @param array $params the parameters of the request
* @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
* with chunks of bytes from the body if the request is successful
*/
public function requestStream($method, $path, $readBodyChunkCallable, $params, $opts)
{
$opts = $this->defaultOpts->merge($opts, true);
$baseUrl = $opts->apiBase ?: $this->getApiBase();
$requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
list($response, $opts->apiKey) = $requestor->requestStream($method, $path, $readBodyChunkCallable, $params, $opts->headers);
}
/**
* Sends a request to Stripe's API.
*
@ -163,6 +182,30 @@ class BaseStripeClient implements StripeClientInterface
return $obj;
}
/**
* Sends a request to Stripe's API.
*
* @param string $method the HTTP method
* @param string $path the path of the request
* @param array $params the parameters of the request
* @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
*
* @return \Stripe\SearchResult of ApiResources
*/
public function requestSearchResult($method, $path, $params, $opts)
{
$obj = $this->request($method, $path, $params, $opts);
if (!($obj instanceof \Stripe\SearchResult)) {
$received_class = \get_class($obj);
$msg = "Expected to receive `Stripe\\SearchResult` object from Stripe API. Instead received `{$received_class}`.";
throw new \Stripe\Exception\UnexpectedValueException($msg);
}
$obj->setFilters($params);
return $obj;
}
/**
* @param \Stripe\Util\RequestOptions $opts
*
@ -260,7 +303,10 @@ class BaseStripeClient implements StripeClientInterface
// check absence of extra keys
$extraConfigKeys = \array_diff(\array_keys($config), \array_keys($this->getDefaultConfig()));
if (!empty($extraConfigKeys)) {
throw new \Stripe\Exception\InvalidArgumentException('Found unknown key(s) in configuration array: ' . \implode(',', $extraConfigKeys));
// Wrap in single quote to more easily catch trailing spaces errors
$invalidKeys = "'" . \implode("', '", $extraConfigKeys) . "'";
throw new \Stripe\Exception\InvalidArgumentException('Found unknown key(s) in configuration array: ' . $invalidKeys);
}
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Stripe;
/**
* Interface for a Stripe client.
*/
interface BaseStripeClientInterface
{
/**
* Gets the API key used by the client to send requests.
*
* @return null|string the API key used by the client to send requests
*/
public function getApiKey();
/**
* Gets the client ID used by the client in OAuth requests.
*
* @return null|string the client ID used by the client in OAuth requests
*/
public function getClientId();
/**
* Gets the base URL for Stripe's API.
*
* @return string the base URL for Stripe's API
*/
public function getApiBase();
/**
* Gets the base URL for Stripe's OAuth API.
*
* @return string the base URL for Stripe's OAuth API
*/
public function getConnectBase();
/**
* Gets the base URL for Stripe's Files API.
*
* @return string the base URL for Stripe's Files API
*/
public function getFilesBase();
}

Some files were not shown because too many files have changed in this diff Show More