├── cartridges ├── int_stripe_sfra │ ├── package.json │ ├── cartridge │ │ ├── int_stripe_sfra.properties │ │ ├── templates │ │ │ └── default │ │ │ │ ├── checkout │ │ │ │ ├── billing │ │ │ │ │ └── paymentOptions │ │ │ │ │ │ ├── APMSummary.isml │ │ │ │ │ │ ├── stripeBankTransferElementContent.isml │ │ │ │ │ │ └── stripePaymentElementContent.isml │ │ │ │ └── paymentelementorderplaced.isml │ │ │ │ └── loadStripe.isml │ │ ├── scripts │ │ │ ├── loadStripe.js │ │ │ └── hooks │ │ │ │ └── payment │ │ │ │ └── processor │ │ │ │ ├── stripe_apm.js │ │ │ │ └── stripe_credit.js │ │ └── controllers │ │ │ ├── Stripe.js │ │ │ └── StripeWallet.js │ ├── .project │ └── hooks.json ├── app_stripe_core │ ├── package.json │ ├── cartridge │ │ ├── templates │ │ │ ├── resources │ │ │ │ └── stripe.properties │ │ │ └── default │ │ │ │ ├── components │ │ │ │ └── footer │ │ │ │ │ └── footer_UI.isml │ │ │ │ ├── checkout │ │ │ │ ├── confirmation │ │ │ │ │ └── confirmation.isml │ │ │ │ └── expressCheckout │ │ │ │ │ └── stripeExpressCheckoutButton.isml │ │ │ │ └── account │ │ │ │ └── payment │ │ │ │ ├── paymentinstrumentlist.isml │ │ │ │ └── paymentinstrumentdetails.isml │ │ ├── scss │ │ │ └── default │ │ │ │ └── stripe.scss │ │ ├── app_stripe_core.properties │ │ ├── static │ │ │ └── default │ │ │ │ └── js │ │ │ │ └── stripe.newcardform.js │ │ └── scripts │ │ │ └── checkout │ │ │ └── ValidatePaymentInstruments.ds │ └── .project ├── int_stripe_core │ ├── package.json │ ├── cartridge │ │ ├── templates │ │ │ ├── default │ │ │ │ └── stripe │ │ │ │ │ ├── util │ │ │ │ │ └── apple.isml │ │ │ │ │ ├── checkout │ │ │ │ │ └── billing │ │ │ │ │ │ ├── stripe_custom_cardform.isml │ │ │ │ │ │ ├── altpaymentmethods.isml │ │ │ │ │ │ ├── cardform.isml │ │ │ │ │ │ └── sfra_cardform.isml │ │ │ │ │ ├── account │ │ │ │ │ └── payment │ │ │ │ │ │ ├── makedefaultform.isml │ │ │ │ │ │ └── newcardform.isml │ │ │ │ │ ├── footerinclude.isml │ │ │ │ │ └── mail │ │ │ │ │ ├── orderfailed.isml │ │ │ │ │ └── orderreceived.isml │ │ │ └── resources │ │ │ │ └── stripe.properties │ │ ├── int_stripe_core.properties │ │ └── scripts │ │ │ └── stripe │ │ │ ├── hooks.json │ │ │ ├── helpers │ │ │ ├── controllers │ │ │ │ ├── stripeWalletHelper.js │ │ │ │ └── stripePaymentsHelper.js │ │ │ ├── cardsHelper.js │ │ │ └── paymentprocessors │ │ │ │ ├── stripeApmHelper.js │ │ │ │ └── stripeCreditHelper.js │ │ │ ├── jobs │ │ │ └── deleteCustomObjectsStep.js │ │ │ └── models │ │ │ └── customerPaymentInstrument.js │ ├── .project │ └── steptypes.json ├── app_stripe_controllers │ ├── package.json │ ├── .project │ ├── cartridge │ │ ├── app_stripe_controllers.properties │ │ ├── controllers │ │ │ ├── RedirectURL.js │ │ │ └── COSummary.js │ │ ├── .eslintrc.json │ │ └── scripts │ │ │ └── payment │ │ │ └── common.js │ └── README.md ├── int_stripe_controllers │ ├── package.json │ ├── cartridge │ │ ├── int_stripe_controllers.properties │ │ ├── scripts │ │ │ └── stripe │ │ │ │ ├── hooks.json │ │ │ │ └── payment │ │ │ │ └── processor │ │ │ │ ├── STRIPE_CREDIT.js │ │ │ │ └── STRIPE_APM.js │ │ └── controllers │ │ │ ├── StripeWallet.js │ │ │ └── Stripe.js │ └── .project ├── app_stripe_sfra │ ├── cartridge │ │ ├── client │ │ │ └── default │ │ │ │ ├── scss │ │ │ │ └── checkout │ │ │ │ │ ├── checkout.scss │ │ │ │ │ └── _stripe.scss │ │ │ │ └── js │ │ │ │ ├── checkout.js │ │ │ │ ├── checkout │ │ │ │ └── checkout.js │ │ │ │ └── stripe.newcardform.js │ │ ├── app_stripe_sfra.properties │ │ ├── templates │ │ │ └── default │ │ │ │ ├── checkout │ │ │ │ ├── billing │ │ │ │ │ ├── paymentOptions │ │ │ │ │ │ ├── paymentOptionsSummary.isml │ │ │ │ │ │ └── paymentOptionsTabs.isml │ │ │ │ │ └── paymentOptions.isml │ │ │ │ ├── expressCheckout │ │ │ │ │ ├── stripeMinicartExpressCheckoutButton.isml │ │ │ │ │ └── stripeExpressCheckoutButton.isml │ │ │ │ └── confirmation │ │ │ │ │ └── confirmation.isml │ │ │ │ └── account │ │ │ │ └── payment │ │ │ │ └── paymentForm.isml │ │ ├── controllers │ │ │ ├── RedirectURL.js │ │ │ ├── PaymentInstruments.js │ │ │ └── Account.js │ │ └── models │ │ │ ├── totals.js │ │ │ └── payment.js │ └── .project └── bm_stripe │ ├── cartridge │ ├── bm_stripe.properties │ ├── templates │ │ ├── default │ │ │ ├── order │ │ │ │ ├── PaymentInstrumentInfo_STRIPE_APM.isml │ │ │ │ ├── PaymentInstrumentInfo_STRIPE_CREDIT.isml │ │ │ │ └── striperadar.isml │ │ │ └── stripebm │ │ │ │ ├── paymentsrefund.isml │ │ │ │ ├── paymentscapture.isml │ │ │ │ ├── paymentssetup.isml │ │ │ │ └── quicksetup.isml │ │ └── resources │ │ │ └── stripebm.properties │ ├── scripts │ │ └── helpers │ │ │ └── stripeBmHelper.js │ └── bm_extensions.xml │ └── .project ├── .babelrc ├── .stylelintrc.json ├── .gitignore ├── .eslintignore ├── tests ├── mocks │ ├── dw-mocks │ │ └── dw │ │ │ ├── svc │ │ │ └── LocalServiceRegistry.js │ │ │ ├── util │ │ │ ├── dw.util.Collection.js │ │ │ └── Calendar.js │ │ │ └── system │ │ │ └── Site.js │ └── int_stripe_core │ │ └── cartridge │ │ └── scripts │ │ └── stripe │ │ └── services │ │ └── StripeService.js └── integration │ ├── pages │ ├── orderPage.js │ ├── stripeAlipayPage.js │ ├── cartPage.js │ ├── basePage.js │ ├── secure3DPage.js │ └── productPage.js │ ├── specs │ └── productSpec.js │ └── data │ └── addresses.js ├── .project ├── .eslintrc.json ├── Readme.md ├── metadata └── stripe_site_template │ ├── sites │ └── siteIDHere │ │ ├── payment-processors.xml │ │ └── payment-methods.xml │ ├── services.xml │ └── jobs.xml ├── LICENSE ├── webpack.config.js ├── package.json ├── protractor-conf.js └── CHANGELOG.md /cartridges/int_stripe_sfra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": "./hooks.json" 3 | } 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "latest", 4 | "stage-2" 5 | ] 6 | } -------------------------------------------------------------------------------- /cartridges/app_stripe_core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": "./cartridge/scripts/hooks.json" 3 | } -------------------------------------------------------------------------------- /cartridges/int_stripe_core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": "./cartridge/scripts/stripe/hooks.json" 3 | } -------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": "./cartridge/scripts/hooks.json" 3 | } 4 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": "./cartridge/scripts/stripe/hooks.json" 3 | } -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "plugins": [ 4 | "stylelint-scss" 5 | ] 6 | } -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/client/default/scss/checkout/checkout.scss: -------------------------------------------------------------------------------- 1 | @import "~base/checkout/checkout"; 2 | @import "stripe"; -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/templates/resources/stripe.properties: -------------------------------------------------------------------------------- 1 | date.of.birth= Date of Birth: 2 | creditcard.detailslabel=Card Details -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/util/apple.isml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cartridges/app_stripe_sfra/cartridge/static/ 2 | node_modules/ 3 | dw.json 4 | .idea/* 5 | demandware.iml 6 | .tern* 7 | .DS_STORE 8 | nbproject/ 9 | .vscode/ 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | cartridges/app_stripe_sfra/cartridge/static/ 2 | cartridges/app_stripe_core/cartridge/static/ 3 | cartridges/int_stripe_core/cartridge/static/ 4 | documentation/ 5 | tests/ 6 | protractor-conf.js 7 | webpack.config.js -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/client/default/scss/checkout/_stripe.scss: -------------------------------------------------------------------------------- 1 | .payment-elements { 2 | &-input{ 3 | padding-top: 0.5625rem; 4 | } 5 | 6 | &-select{ 7 | padding: 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/bm_stripe.properties: -------------------------------------------------------------------------------- 1 | ## cartridge.properties for cartridge bm_stripe 2 | #Tue Jun 04 11:10:21 BST 2019 3 | demandware.cartridges.bm_stripe.id=bm_stripe 4 | demandware.cartridges.bm_stripe.multipleLanguageStorefront=true 5 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/client/default/js/checkout.js: -------------------------------------------------------------------------------- 1 | /* global $ */ 2 | 3 | 'use strict'; 4 | 5 | var processInclude = require('base/util'); 6 | 7 | $(document).ready(function () { 8 | processInclude(require('./checkout/checkout')); 9 | }); 10 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/cartridge/int_stripe_controllers.properties: -------------------------------------------------------------------------------- 1 | ## cartridge.properties for cartridge int_stripe 2 | #Tue Jun 04 11:10:45 BST 2019 3 | demandware.cartridges.int_stripe.id=int_stripe 4 | demandware.cartridges.int_stripe.multipleLanguageStorefront=true 5 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/app_stripe_sfra.properties: -------------------------------------------------------------------------------- 1 | ## cartridge.properties for cartridge app_stripe_sfra 2 | #Tue Jun 04 11:11:02 BST 2019 3 | demandware.cartridges.app_stripe_sfra.id=app_stripe_sfra 4 | demandware.cartridges.app_stripe_sfra.multipleLanguageStorefront=true 5 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/int_stripe_core.properties: -------------------------------------------------------------------------------- 1 | ## cartridge.properties for cartridge int_stripe_core 2 | #Tue Jun 04 11:10:21 BST 2019 3 | demandware.cartridges.int_stripe_core.id=int_stripe_core 4 | demandware.cartridges.int_stripe_core.multipleLanguageStorefront=true 5 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/int_stripe_sfra.properties: -------------------------------------------------------------------------------- 1 | ## cartridge.properties for cartridge int_stripe_sfra 2 | #Tue Jun 04 11:11:02 BST 2019 3 | demandware.cartridges.int_stripe_sfra.id=int_stripe_sfra 4 | demandware.cartridges.int_stripe_sfra.multipleLanguageStorefront=true 5 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/scss/default/stripe.scss: -------------------------------------------------------------------------------- 1 | $dark-gray: #444; 2 | 3 | .payment-elements { 4 | &-input { 5 | border: 0.0625rem solid $dark-gray; 6 | padding: 0.375rem; 7 | } 8 | 9 | &-select { 10 | border: 0.0625rem solid $dark-gray; 11 | } 12 | } -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/templates/default/checkout/billing/paymentOptions/APMSummary.isml: -------------------------------------------------------------------------------- 1 |
2 | ${Resource.msg('payment.pay.with', 'stripe', null)} 3 | ${dw.order.PaymentMgr.getPaymentMethod(payment.paymentMethod).name} 4 |
5 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/hooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": [ 3 | { 4 | "name": "dw.order.payment.authorizeCreditCard", 5 | "script":"./hooks/authorizeCSC.js" 6 | }, 7 | { 8 | "name": "dw.order.payment.authorize", 9 | "script":"./hooks/authorizeCSC.js" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/scripts/loadStripe.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | 3 | 'use strict'; 4 | 5 | /** 6 | * @type {dw.template.ISML} 7 | */ 8 | const ISML = require('dw/template/ISML'); 9 | 10 | /** 11 | * Load stripe js lib 12 | */ 13 | function htmlHead() { 14 | ISML.renderTemplate('loadStripe'); 15 | } 16 | 17 | exports.htmlHead = htmlHead; 18 | -------------------------------------------------------------------------------- /tests/mocks/dw-mocks/dw/svc/LocalServiceRegistry.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | const LocalServiceRegistry = { 4 | 5 | call: sinon.stub(), 6 | 7 | createService(serviceId, callback) { 8 | return { 9 | serviceId, 10 | callback, 11 | call: this.call 12 | }; 13 | } 14 | }; 15 | 16 | module.exports = LocalServiceRegistry; 17 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/default/order/PaymentInstrumentInfo_STRIPE_APM.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/default/order/PaymentInstrumentInfo_STRIPE_CREDIT.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/cartridge/scripts/stripe/hooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": [ 3 | { 4 | "name": "app.payment.processor.STRIPE_CREDIT", 5 | "script": "./payment/processor/STRIPE_CREDIT" 6 | }, 7 | { 8 | "name": "app.payment.processor.STRIPE_APM", 9 | "script": "./payment/processor/STRIPE_APM" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | link_stripe 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.validation.validationbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.wst.jsdt.core.jsNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/app_stripe_core.properties: -------------------------------------------------------------------------------- 1 | ## cartridge.properties for cartridge app_stripe_core 2 | #Tue Jun 04 11:06:42 BST 2019 3 | demandware.cartridges.app_stripe_core.coreID=app_stripe_core 4 | demandware.cartridges.app_stripe_core.multipleLanguageStorefront=true 5 | demandware.cartridges.app_stripe_core.id=app_stripe 6 | demandware.cartridges.app_stripe_core.pipelinesID=app_stripe_pipelines 7 | demandware.cartridges.app_stripe_core.controllersID=app_stripe_controllers 8 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | bm_stripe 4 | 5 | 6 | 7 | 8 | 9 | com.demandware.studio.core.beehiveElementBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.demandware.studio.core.beehiveNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app_stripe_core 4 | 5 | 6 | 7 | 8 | 9 | com.demandware.studio.core.beehiveElementBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.demandware.studio.core.beehiveNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app_stripe_sfra 4 | 5 | 6 | 7 | 8 | 9 | com.demandware.studio.core.beehiveElementBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.demandware.studio.core.beehiveNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | int_stripe_core 4 | 5 | 6 | 7 | 8 | 9 | com.demandware.studio.core.beehiveElementBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.demandware.studio.core.beehiveNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | int_stripe_sfra 4 | 5 | 6 | 7 | 8 | 9 | com.demandware.studio.core.beehiveElementBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.demandware.studio.core.beehiveNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/integration/pages/orderPage.js: -------------------------------------------------------------------------------- 1 | import BasePage from './basePage'; 2 | 3 | class OrderPage extends BasePage { 4 | constructor() { 5 | super(); 6 | this.paymentDetails = $('.credit-card-type'); 7 | this.url = browser.baseUrl + 'Order-Confirm'; 8 | this.pageLoaded = this.inDom($('.order-confirmation-continue-shopping')); 9 | } 10 | 11 | async getPaymentDetails() { 12 | return this.paymentDetails.getText(); 13 | } 14 | } 15 | export default OrderPage; 16 | -------------------------------------------------------------------------------- /tests/mocks/int_stripe_core/cartridge/scripts/stripe/services/StripeService.js: -------------------------------------------------------------------------------- 1 | const sandboxedModule = require('sandboxed-module'); 2 | module.exports = sandboxedModule.load('../../../../../../../cartridges/int_stripe_core/cartridge/scripts/stripe/services/stripeService', { 3 | requires: { 4 | 'dw/svc/LocalServiceRegistry': require('../../../../../dw-mocks/dw/svc/LocalServiceRegistry'), 5 | 'dw/system/Site': require('../../../../../dw-mocks/dw/system/Site') 6 | }, 7 | singleOnly: true 8 | }); 9 | -------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app_stripe_controllers 4 | 5 | 6 | 7 | 8 | 9 | com.demandware.studio.core.beehiveElementBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.demandware.studio.core.beehiveNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | int_stripe_controllers 4 | 5 | 6 | 7 | 8 | 9 | com.demandware.studio.core.beehiveElementBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.demandware.studio.core.beehiveNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/cartridge/app_stripe_controllers.properties: -------------------------------------------------------------------------------- 1 | ## cartridge.properties for cartridge app_stripe_controllers 2 | #Tue Jun 04 11:06:41 BST 2019 3 | demandware.cartridges.app_stripe_controllers.multipleLanguageStorefront=true 4 | demandware.cartridges.app_stripe_controllers.id=app_stripe 5 | demandware.cartridges.app_stripe_controllers.pipelinesID=app_stripe_pipelines 6 | demandware.cartridges.app_stripe_controllers.coreID=app_stripe_core 7 | demandware.cartridges.app_stripe_controllers.controllersID=app_stripe_controllers 8 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/templates/default/checkout/billing/paymentOptions/paymentOptionsSummary.isml: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
-------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "airbnb-base/legacy", 4 | "rules": { 5 | "import/no-unresolved": "off", 6 | "indent": ["error", 4, { "SwitchCase": 1, "VariableDeclarator": 1 }], 7 | "func-names": "off", 8 | "require-jsdoc": "error", 9 | "valid-jsdoc": ["error", { "preferType": { "Boolean": "boolean", "Number": "number", "object": "Object", "String": "string" }, "requireReturn": false}], 10 | "vars-on-top": "off", 11 | "global-require": "off", 12 | "no-shadow": ["error", { "allow": ["err", "callback"]}], 13 | "max-len": "off" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/templates/default/checkout/billing/paymentOptions/stripeBankTransferElementContent.isml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 5 |
6 |
7 |
8 |
9 |
10 |
-------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # STRIPE Card Form 2 | 23.7.0 - Starting with future releases Card Form will become depprecated. 3 | 4 | # Stripe Salesforce Commerce Cloud Cartridge 5 | 6 | This repository contains the Stripe integrations with the Salesforce Commerce Cloud platform. There are two versions currently available for SiteGenesis Javascript Controller (SGJS) and Salesforce Reference Architecture (SFRA) which have the following features: 7 | 8 | 1. Card payments 9 | 2. Alternative payment methods 10 | 3. Customer wallet 11 | 12 | ## Visit our [Documentation](https://docs.stripe.com/use-stripe-apps/salesforce-commerce-cloud) to start using the Stripe Salesforce B2C Connector. 13 | -------------------------------------------------------------------------------- /tests/integration/pages/stripeAlipayPage.js: -------------------------------------------------------------------------------- 1 | import BasePage from './basePage'; 2 | 3 | class StripeAlipayPage extends BasePage { 4 | constructor() { 5 | super(); 6 | this.authorize = $('section.source').element(by.partialButtonText('Authorize')); 7 | this.fail = $('section.source').element(by.partialButtonText('Fail')); 8 | this.url = 'https://stripe.com/sources/test_source'; 9 | this.pageLoaded = this.inDom($('.object')); 10 | } 11 | 12 | async clickAuthorize() { 13 | return this.authorize.click(); 14 | } 15 | 16 | async clickFail() { 17 | return this.fail.click(); 18 | } 19 | } 20 | export default StripeAlipayPage; 21 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/templates/default/checkout/billing/paymentOptions/stripePaymentElementContent.isml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 5 | 6 |
7 |
8 |
9 |
10 |
11 |
-------------------------------------------------------------------------------- /metadata/stripe_site_template/sites/siteIDHere/payment-processors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/controllers/RedirectURL.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var server = require('server'); 4 | var page = module.superModule; 5 | server.extend(page); 6 | 7 | server.prepend('Start', function (req, res, next) { 8 | var URLRedirectMgr = require('dw/web/URLRedirectMgr'); 9 | var stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 10 | 11 | // Stripe changes BEGIN 12 | if (stripeHelper.isStripeEnabled() && URLRedirectMgr.getRedirectOrigin() === '/.well-known/apple-developer-merchantid-domain-association') { // Intercept the incoming path request 13 | res.render('stripe/util/apple'); 14 | return null; 15 | } 16 | 17 | return next(); 18 | }); 19 | 20 | module.exports = server.exports(); 21 | -------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Next Generation SiteGenesis 2 | 3 | This SiteGenesis is now fully standard Javascript compliant using the new [Controllers]{@tutorial Controllers} concept to handle incoming requests. The controllers integrate nicely into a completely redesigned MVC concept which also includes [Models]{@tutorial Models} and [Views]{@tutorial Views}. All scripts are [Common JS modules](http://www.commonjs.org) with defined and documented exports to avoid polluting the global namespace. 4 | 5 | This documentation is meant to serve as a reference to quickly lookup supported functionality and is fully based on the documentation inside the code. You can continue to maintain these [JSDoc comments](http://usejsdoc.org/) to generate a similar documentation for your own project. 6 | 7 | Enjoy! -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/checkout/billing/stripe_custom_cardform.isml: -------------------------------------------------------------------------------- 1 |
3 |
4 | ${Resource.msg('customcardform.cardnumber', 'stripe', null)} 5 |
6 | 7 |
8 |
9 | ${Resource.msg('customcardform.expirydate', 'stripe', null)} 10 |
11 |
12 |
13 | ${Resource.msg('customcardform.cvc', 'stripe', null)} 14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/hooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": [ 3 | { 4 | "name": "app.template.htmlHead", 5 | "script": "./cartridge/scripts/loadStripe" 6 | }, 7 | { 8 | "name": "app.payment.processor.stripe_credit", 9 | "script": "./cartridge/scripts/hooks/payment/processor/stripe_credit" 10 | }, 11 | { 12 | "name": "app.payment.form.processor.stripe_credit", 13 | "script": "./cartridge/scripts/hooks/payment/processor/stripe_credit" 14 | }, 15 | { 16 | "name": "app.payment.processor.stripe_apm", 17 | "script": "./cartridge/scripts/hooks/payment/processor/stripe_apm" 18 | }, 19 | { 20 | "name": "app.payment.form.processor.stripe_apm", 21 | "script": "./cartridge/scripts/hooks/payment/processor/stripe_apm" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/controllers/Stripe.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | 3 | 'use strict'; 4 | 5 | var server = require('server'); 6 | 7 | server.get('GetShippingOptions', function (req, res, next) { 8 | res.json(require('*/cartridge/scripts/stripe/helpers/checkoutHelper').getShippingOptionsSFRA(req.querystring)); 9 | next(); 10 | }); 11 | 12 | server.post('WebHook', function (req, res, next) { 13 | const webhooksHelper = require('*/cartridge/scripts/stripe/helpers/webhooksHelper'); 14 | var success = webhooksHelper.processIncomingNotification(); 15 | 16 | res.setStatusCode(success ? 200 : 500); 17 | res.json({ 18 | success: !!success 19 | }); 20 | next(); 21 | }); 22 | 23 | server.get('PaymentElementOrderPlaced', function (req, res, next) { 24 | res.render('checkout/paymentelementorderplaced.isml', {}); 25 | next(); 26 | }); 27 | 28 | module.exports = server.exports(); 29 | -------------------------------------------------------------------------------- /metadata/stripe_site_template/sites/siteIDHere/payment-methods.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Credit Card 6 | Carte de crédit 7 | Carta di credito 8 | クレジットカード 9 | 信用卡 10 | true 11 | STRIPE_CREDIT 12 | 13 | 14 | 15 | New Payment Method 16 | true 17 | STRIPE_APM 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/controllers/StripeWallet.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable new-cap */ 2 | /* global response */ 3 | 4 | 'use strict'; 5 | 6 | var server = require('server'); 7 | 8 | var URLUtils = require('dw/web/URLUtils'); 9 | var stripeWalletHelper = require('*/cartridge/scripts/stripe/helpers/controllers/stripeWalletHelper'); 10 | var csrfProtection = require('*/cartridge/scripts/middleware/csrf'); 11 | 12 | /** 13 | * AddNewCard controller to handle AJAX calls 14 | */ 15 | server.post('AddNewCard', csrfProtection.validateAjaxRequest, function (req, res, next) { 16 | var result = stripeWalletHelper.AddNewCard(); 17 | res.json(result); 18 | next(); 19 | }); 20 | 21 | /** 22 | * Makes a card default. 23 | */ 24 | server.post('MakeDefault', csrfProtection.validateAjaxRequest, function (req, res, next) { 25 | stripeWalletHelper.MakeDefault(); 26 | response.redirect(URLUtils.https('PaymentInstruments-List')); 27 | next(); 28 | }); 29 | 30 | module.exports = server.exports(); 31 | -------------------------------------------------------------------------------- /tests/integration/pages/cartPage.js: -------------------------------------------------------------------------------- 1 | import BasePage from './basePage'; 2 | import { browser } from 'protractor'; 3 | 4 | class CartPage extends BasePage { 5 | constructor() { 6 | super(); 7 | this.removeButtons = $$('button[data-target=".cart.cart-page #removeProductModal"]'); 8 | this.removeModalButtonYes = $('#removeProductModal').element(by.buttonText('Yes')); 9 | this.total = $$('.minicart-quantity').first(); 10 | this.url = browser.baseUrl + 'Cart-Show'; 11 | } 12 | 13 | async deleteProductFromCart() { 14 | let EC = await protractor.ExpectedConditions; 15 | this.removeButtons.each(async (btn) => { 16 | if (await btn.isDisplayed()) { 17 | await btn.click(); 18 | await browser.wait(EC.elementToBeClickable(this.removeModalButtonYes), 10000); 19 | await this.removeModalButtonYes.click(); 20 | } 21 | }); 22 | 23 | await browser.wait(EC.textToBePresentInElement(this.total, '0'), 10000); 24 | } 25 | } 26 | export default CartPage; 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Stripe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/integration/pages/basePage.js: -------------------------------------------------------------------------------- 1 | import { browser } from 'protractor'; 2 | 3 | export default class BasePage { 4 | constructor() { 5 | this.timeout = 20000; 6 | // increase this value if tests are failing 7 | this.baseSleep = 2000; 8 | } 9 | 10 | // eslint-disable-next-line valid-jsdoc 11 | /** 12 | * navigate to a page via it's `url` var 13 | * @requires page have both `url` 14 | */ 15 | async goto() { 16 | await browser.get(this.url); 17 | if (await $('#consent-tracking').isPresent()) { 18 | let EC = protractor.ExpectedConditions; 19 | await $('.button-wrapper .affirm').click(); 20 | await browser.wait(EC.invisibilityOf($('#consent-tracking')), this.timeout); 21 | } 22 | } 23 | 24 | // eslint-disable-next-line class-methods-use-this 25 | inDom(locator) { 26 | return protractor.ExpectedConditions.presenceOf(locator); 27 | } 28 | 29 | async loaded() { 30 | return browser.wait(async () => { 31 | return this.pageLoaded(); 32 | }, this.timeout, 'timeout: waiting for page to load. The url is: ' + this.url); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/integration/specs/productSpec.js: -------------------------------------------------------------------------------- 1 | import ProductPage from '../pages/productPage'; 2 | import CartPage from '../pages/cartPage'; 3 | 4 | describe('Login', () => { 5 | const productPage = new ProductPage('25519318M'); 6 | const cartPage = new CartPage(); 7 | 8 | beforeEach(async () => { 9 | await productPage.goto(); 10 | }); 11 | 12 | it('should display product name', async () => { 13 | const productNames = await productPage.productName.getText(); 14 | let productName = (productNames[0]) ? productNames[0] : productNames[1]; 15 | expect(productName).toEqual('3/4 Sleeve V-Neck Top'); 16 | }); 17 | 18 | it('should add to cart product', async () => { 19 | await productPage.addProductToCart(); 20 | expect(await productPage.total.getText()).toBe('1'); 21 | }); 22 | 23 | it('should add and remove product from to cart', async () => { 24 | await productPage.addProductToCart(); 25 | expect(await productPage.total.getText()).toBe('1'); 26 | await cartPage.goto(); 27 | await cartPage.deleteProductFromCart(); 28 | expect(await cartPage.total.getText()).toBe('0'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /metadata/stripe_site_template/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://api.stripe.com/v1 5 | 6 | 7 | 8 | 3000 9 | true 10 | 5 11 | 100 12 | true 13 | 5 14 | 500 15 | 16 | 17 | 18 | HTTP 19 | true 20 | Stripe 21 | true 22 | false 23 | false 24 | StripeProfile 25 | StripeCredentials 26 | 27 | -------------------------------------------------------------------------------- /tests/integration/pages/secure3DPage.js: -------------------------------------------------------------------------------- 1 | import BasePage from './basePage'; 2 | 3 | class Secure3DPage extends BasePage { 4 | constructor() { 5 | super(); 6 | this.completeButton = $('#test-source-authorize-3ds'); 7 | this.failButton = $('#test-source-fail-3ds'); 8 | this.pageLoaded = this.inDom($('iframe[name="__privateStripeFrame8"]')); 9 | } 10 | 11 | async clickAuthorize() { 12 | await browser.sleep(this.baseSleep); 13 | await browser.switchTo().frame($('iframe[name="__privateStripeFrame8"]').getWebElement()); 14 | await browser.wait(await protractor.ExpectedConditions.presenceOf($('iframe')), this.timeout, 'timeout: waiting for load 3D Secure modal frame'); 15 | await browser.sleep(this.baseSleep); 16 | await browser.switchTo().frame($('iframe').getWebElement()); 17 | await browser.wait(await protractor.ExpectedConditions.presenceOf(this.completeButton), this.timeout, 'timeout: waiting for load 3D Secure modal complate button'); 18 | await browser.sleep(this.baseSleep); 19 | await this.completeButton.click(); 20 | await browser.switchTo().defaultContent(); 21 | } 22 | } 23 | export default Secure3DPage; 24 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/cartridge/scripts/stripe/payment/processor/STRIPE_CREDIT.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* eslint-disable new-cap */ 3 | // v1 4 | 5 | 'use strict'; 6 | 7 | const stripeCreditHelper = require('*/cartridge/scripts/stripe/helpers/paymentprocessors/stripeCreditHelper'); 8 | 9 | /** 10 | * Verifies a credit card against a valid card number and expiration date and possibly invalidates invalid form fields. 11 | * If the verification was successful a credit card payment instrument is created. 12 | * 13 | * @param {Object} args - Pipeline dictionary parameters 14 | * @returns {Object} - Result 15 | */ 16 | function Handle(args) { 17 | return stripeCreditHelper.Handle(args); 18 | } 19 | 20 | /** 21 | * Authorizes a payment using a credit card. The payment is authorized by using the BASIC_CREDIT processor 22 | * only and setting the order no as the transaction ID. Customizations may use other processors and custom 23 | * logic to authorize credit card payment. 24 | * 25 | * @param {Object} args - Pipeline dictionary parameters 26 | * @returns {Object} - Result 27 | */ 28 | function Authorize(args) { 29 | return stripeCreditHelper.Authorize(args); 30 | } 31 | 32 | exports.Handle = Handle; 33 | exports.Authorize = Authorize; 34 | -------------------------------------------------------------------------------- /tests/integration/pages/productPage.js: -------------------------------------------------------------------------------- 1 | import BasePage from './basePage'; 2 | import { browser } from 'protractor'; 3 | 4 | class ProductPage extends BasePage { 5 | constructor(productId) { 6 | super(); 7 | this.productName = $$('.product-name'); 8 | this.attributeColorButtons = $$('.attributes div[data-attr="color"] button'); 9 | this.attributeSizeOptions = $('.attributes div[data-attr="size"] .select-size').$$('option'); 10 | this.addToCartButton = $('.prices-add-to-cart-actions button'); 11 | this.total = $$('.minicart-quantity').first(); 12 | this.url = browser.baseUrl + 'Product-Show?pid=' + productId; 13 | } 14 | 15 | async addProductToCart() { 16 | let EC = await protractor.ExpectedConditions; 17 | this.attributeColorButtons.get(0).click(); 18 | await browser.wait(EC.presenceOf(this.attributeColorButtons.get(0).$('.selected')), this.timeout); 19 | this.attributeSizeOptions.get(1).click(); 20 | await browser.wait(EC.elementToBeClickable(this.addToCartButton), this.timeout); 21 | this.addToCartButton.click(); 22 | await browser.wait(EC.textToBePresentInElement(this.total, '1'), this.timeout); 23 | } 24 | } 25 | export default ProductPage; 26 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/client/default/js/checkout/checkout.js: -------------------------------------------------------------------------------- 1 | /* global $ */ 2 | 3 | 'use strict'; 4 | 5 | var base = require('base/checkout/checkout'); 6 | 7 | var billingHelpers = require('./billing'); 8 | var shippingHelpers = require('base/checkout/shipping'); 9 | var summaryHelpers = require('base/checkout/summary'); 10 | 11 | base.updateCheckoutView = function () { 12 | $('body').on('checkout:updateCheckoutView', function (e, data) { 13 | shippingHelpers.methods.updateMultiShipInformation(data.order); 14 | summaryHelpers.updateTotals(data.order.totals); 15 | 16 | data.order.shipping.forEach(function (shipping) { 17 | shippingHelpers.methods.updateShippingInformation( 18 | shipping, 19 | data.order, 20 | data.customer, 21 | data.options 22 | ); 23 | }); 24 | 25 | billingHelpers.methods.updateBillingInformation( 26 | data.order, 27 | data.customer, 28 | data.options 29 | ); 30 | 31 | billingHelpers.methods.updatePaymentInformation(data.order, data.options); 32 | summaryHelpers.updateOrderProductSummaryInformation(data.order, data.options); 33 | }); 34 | }; 35 | 36 | module.exports = base; 37 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/templates/default/checkout/paymentelementorderplaced.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/templates/default/loadStripe.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/models/totals.js: -------------------------------------------------------------------------------- 1 | /* global dw */ 2 | 3 | 'use strict'; 4 | 5 | var base = module.superModule; 6 | 7 | /** 8 | * Accepts a total object and formats the value 9 | * @param {dw.value.Money} total - Total price of the cart 10 | * @returns {integer} the formatted money value 11 | */ 12 | function getTotalsValue(total) { 13 | if (!total.available) { 14 | return 0; 15 | } 16 | 17 | var currentCurency = dw.util.Currency.getCurrency(total.getCurrencyCode()); 18 | var multiplier = Math.pow(10, currentCurency.getDefaultFractionDigits()); 19 | 20 | return parseInt(total.value * multiplier, 10); 21 | } 22 | 23 | /** 24 | * @constructor 25 | * @classdesc totals class that represents the order totals of the current line item container 26 | * 27 | * @param {dw.order.lineItemContainer} lineItemContainer - The current user's line item container 28 | */ 29 | function totals(lineItemContainer) { 30 | base.call(this, lineItemContainer); 31 | 32 | var stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 33 | if (stripeHelper.isStripeEnabled()) { 34 | if (lineItemContainer) { 35 | this.grandTotalValue = getTotalsValue(lineItemContainer.totalGrossPrice); 36 | } else { 37 | this.grandTotalValue = 0; 38 | } 39 | } 40 | } 41 | 42 | module.exports = totals; 43 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/cartridge/scripts/stripe/payment/processor/STRIPE_APM.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* eslint-disable new-cap */ 3 | /* eslint-disable no-unused-vars */ 4 | // v1 5 | 6 | 'use strict'; 7 | 8 | var PaymentMgr = require('dw/order/PaymentMgr'); 9 | var Transaction = require('dw/system/Transaction'); 10 | const stripeApmHelper = require('*/cartridge/scripts/stripe/helpers/paymentprocessors/stripeApmHelper'); 11 | 12 | /** 13 | * Verifies a credit card against a valid card number and expiration date and possibly invalidates invalid form fields. 14 | * If the verification was successful a credit card payment instrument is created. 15 | * 16 | * @param {Object} args - Pipeline dictionary parameters 17 | * @returns {Object} - Result 18 | */ 19 | function Handle(args) { 20 | return stripeApmHelper.Handle(args); 21 | } 22 | 23 | /** 24 | * Authorizes a payment using a credit card. The payment is authorized by using the BASIC_CREDIT processor 25 | * only and setting the order no as the transaction ID. Customizations may use other processors and custom 26 | * logic to authorize credit card payment. 27 | * 28 | * @param {Object} args - Pipeline dictionary parameters 29 | * @returns {Object} - Result 30 | */ 31 | function Authorize(args) { 32 | return stripeApmHelper.Authorize(args); 33 | } 34 | 35 | exports.Handle = Handle; 36 | exports.Authorize = Authorize; 37 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/account/payment/makedefaultform.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
${Resource.msg('account.paymentinstrumentlist.markedasdefault.label','stripe',null)}
7 | 8 |
9 |
10 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /tests/mocks/dw-mocks/dw/util/dw.util.Collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (array) { 4 | var items = []; 5 | if (array) { 6 | items = array; 7 | } 8 | 9 | this.add = function (item) { 10 | items.push(item); 11 | }; 12 | 13 | this.iterator = function () { 14 | var i = 0; 15 | return { 16 | hasNext: function () { 17 | return i < items.length; 18 | }, 19 | next: function () { 20 | return items[i++]; 21 | } 22 | }; 23 | }; 24 | 25 | this.getLength = function () { 26 | return items.length; 27 | }; 28 | 29 | this.length = this.getLength(); 30 | 31 | this.toArray = function () { 32 | return items; 33 | }; 34 | 35 | this.addAll = function (collection) { 36 | items = items.concat(collection.toArray()); 37 | }; 38 | 39 | this.contains = function (item) { 40 | return array.indexOf(item) >= 0; 41 | }; 42 | 43 | this.map = function () { 44 | var args = Array.from(arguments); 45 | var list = args[0]; 46 | var callback = args[1]; 47 | if (list && Object.prototype.hasOwnProperty.call(list, 'toArray')) { 48 | list = list.toArray(); 49 | } 50 | return list ? list.map(callback) : []; 51 | }; 52 | 53 | this.get = function (index) { 54 | return items[index]; 55 | }; 56 | }; 57 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/helpers/controllers/stripeWalletHelper.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* global request, customer */ 3 | 4 | 'use strict'; 5 | 6 | /** 7 | * Add New Card 8 | * 9 | * @returns {array} - array with result info 10 | */ 11 | function AddNewCard() { 12 | const stripePaymentMethodId = request.httpParameterMap.payment_method_id.stringValue; 13 | const stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 14 | const wallet = stripeHelper.getStripeWallet(customer); 15 | 16 | var responsePayload = { 17 | success: true 18 | }; 19 | 20 | try { 21 | wallet.attachPaymentInstrument(stripePaymentMethodId); 22 | } catch (e) { 23 | responsePayload = { 24 | success: false, 25 | error: e.message 26 | }; 27 | } 28 | 29 | return responsePayload; 30 | } 31 | 32 | module.exports.AddNewCard = AddNewCard; 33 | module.exports.AddNewCard.public = true; 34 | 35 | /** 36 | * Make card default 37 | */ 38 | function MakeDefault() { 39 | const stripeId = request.httpParameterMap.stripe_id.stringValue; 40 | const stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 41 | const wallet = stripeHelper.getStripeWallet(customer); 42 | 43 | try { 44 | wallet.makeDefault(stripeId); 45 | } catch (e) { 46 | require('dw/system/Logger').error('Failed to make card default, original error was: {0}', e.message); 47 | } 48 | } 49 | 50 | module.exports.MakeDefault = MakeDefault; 51 | module.exports.MakeDefault.public = true; 52 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/resources/stripe.properties: -------------------------------------------------------------------------------- 1 | 2 | ############################################## 3 | # Card Brand Translations 4 | ############################################## 5 | cardtype.visa=Visa 6 | cardtype.mastercard=Mastercard 7 | cardtype.amex=American Express 8 | cadrtype.discover=Discover 9 | 10 | account.paymentinstrumentlist.makedefaultcard=Make default 11 | account.paymentinstrumentlist.markedasdefault.label=Marked As Default 12 | 13 | stripe.confirmation.thankyou=We have received your order! Payment is Pending. 14 | stripe.confirmation.shippingnote=IMPORTANT! Your order is not complete until payment has been verified. 15 | 16 | order.orderfailed-email.001=Failed of Your Order 17 | order.ordercreceived-email.001=Received Your Order 18 | 19 | method.name.label=Name 20 | method.email.label=Email 21 | 22 | creditcard.use.savedcard=Use a saved card 23 | creditcard.savedcard=Saved cards: 24 | creditcard.add.card=Add a new card 25 | 26 | payment.pay.with=Pay with 27 | 28 | guesscustomer.savecard=Save card on Stripe for re-authorize payments 29 | 30 | customcardform.cardnumber=Card number 31 | customcardform.expirydate=Expiry date 32 | customcardform.cvc=CVC 33 | customcardform.postalcode=Postal code 34 | customcardform.postalcodeplaceholder=90210 35 | 36 | order.amountrefunded=Amount Refunded 37 | order.amountrefundedmsg=Amount {0} was successfully refunded from order #{1}. 38 | 39 | apm.error.fail=The payment you submitted failed. Please re-enter payment information or choose another payment method. 40 | 41 | paymentelement.savedmethod=Saved Payment Method 42 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/cartridge/controllers/StripeWallet.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable new-cap */ 2 | /* global response */ 3 | // v1 4 | 5 | 'use strict'; 6 | 7 | var URLUtils = require('dw/web/URLUtils'); 8 | var CSRFProtection = require('dw/web/CSRFProtection'); 9 | var stripeWalletHelper = require('*/cartridge/scripts/stripe/helpers/controllers/stripeWalletHelper'); 10 | var app = require('*/cartridge/scripts/app'); 11 | 12 | /** 13 | * AddNewCard controller to handle AJAX calls 14 | */ 15 | function AddNewCard() { 16 | var result; 17 | 18 | if (!CSRFProtection.validateRequest()) { 19 | app.getModel('Customer').logout(); 20 | result = { 21 | redirectUrl: URLUtils.url('Home-Show').toString() 22 | }; 23 | response.setStatus(500); 24 | } else { 25 | result = stripeWalletHelper.AddNewCard(); 26 | } 27 | 28 | var jsonResponse = JSON.stringify(result); 29 | response.setContentType('application/json'); 30 | response.writer.print(jsonResponse); 31 | } 32 | 33 | module.exports.AddNewCard = AddNewCard; 34 | module.exports.AddNewCard.public = true; 35 | 36 | /** 37 | * Makes a card default. 38 | */ 39 | function MakeDefault() { 40 | if (!CSRFProtection.validateRequest()) { 41 | app.getModel('Customer').logout(); 42 | response.redirect(URLUtils.https('Home-Show')); 43 | return; 44 | } 45 | stripeWalletHelper.MakeDefault(); 46 | response.redirect(URLUtils.https('PaymentInstruments-List')); 47 | } 48 | 49 | module.exports.MakeDefault = MakeDefault; 50 | module.exports.MakeDefault.public = true; 51 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/account/payment/newcardform.isml: -------------------------------------------------------------------------------- 1 |
5 |
6 |
7 | 8 |
9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 23 | 26 |
27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/default/stripebm/paymentsrefund.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

${Resource.msg('paymentsrefund.title','stripebm', null)}

5 |

${Resource.msg('paymentsrefund.info','stripebm', null)}

6 | 7 |
9 | 10 | 11 | 12 | 13 | 16 | 19 | 20 | 21 | 24 | 27 | 28 | 29 | 32 | 33 | 34 | 37 | 39 | 40 | 41 |
14 | ${Resource.msg('paymentsrefund.ordernumber','stripebm', null)} 15 | 17 | 18 |
22 | ${Resource.msg('paymentsrefund.amounttorefund','stripebm', null)} 23 | 25 | 26 |
30 | 31 |
35 | 36 | 38 |
42 |
43 | 44 | 45 |
-------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/default/stripebm/paymentscapture.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

${Resource.msg('paymentscapture.title','stripebm', null)}

5 |

${Resource.msg('paymentscapture.info','stripebm', null)}

6 | 7 |
9 | 10 | 11 | 12 | 13 | 16 | 19 | 20 | 21 | 24 | 27 | 28 | 29 | 32 | 33 | 34 | 37 | 39 | 40 | 41 |
14 | ${Resource.msg('paymentscapture.ordernumber','stripebm', null)} 15 | 17 | 18 |
22 | ${Resource.msg('paymentscapture.amounttocapture','stripebm', null)} 23 | 25 | 26 |
30 | 31 |
35 | 36 | 38 |
42 |
43 | 44 | 45 |
-------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/default/stripebm/paymentssetup.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

${Resource.msg('paymentssetup.title','stripebm', null)}

6 |

${Resource.msg('paymentssetup.info','stripebm', null)}

7 | 8 |
10 | 11 | 12 | 13 | 14 | 15 | 18 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 36 | 37 | 38 |
16 | ${stripePaymentMethod.name} 17 | 19 | checked /> 22 |
27 | 28 |
32 | 33 | 35 |
39 |
40 | 41 | 42 |
-------------------------------------------------------------------------------- /tests/mocks/dw-mocks/dw/util/Calendar.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const momentJavaSimpleDateFormat = require('moment-jdateformatparser'); 3 | 4 | momentJavaSimpleDateFormat(moment); 5 | 6 | class Calendar { 7 | static get DAY_OF_MONTH() { return 5; } 8 | 9 | static get HOUR_OF_DAY() { return 1; } 10 | 11 | static get MINUTE() { return 12; } 12 | 13 | static get SECOND() { return 13; } 14 | 15 | constructor(date = new Date()) { 16 | this.date = date; 17 | } 18 | 19 | add(field, value) { 20 | switch (field) { 21 | case Calendar.DAY_OF_MONTH: 22 | this.date.setDate(this.date.getDate() + value); 23 | break; 24 | 25 | default: 26 | } 27 | } 28 | 29 | get(field) { 30 | switch (field) { 31 | case Calendar.HOUR_OF_DAY: 32 | return this.date.getHours(); 33 | default: 34 | return null; 35 | } 36 | } 37 | 38 | set(field, value) { 39 | switch (field) { 40 | case Calendar.HOUR_OF_DAY: 41 | this.date.setHours(value); 42 | break; 43 | case Calendar.MINUTE: 44 | this.date.setMinutes(value); 45 | break; 46 | case Calendar.SECOND: 47 | this.date.setSeconds(value); 48 | break; 49 | default: 50 | } 51 | } 52 | 53 | getTime() { 54 | return this.date; 55 | } 56 | 57 | // eslint-disable-next-line class-methods-use-this 58 | parseByFormat(dateString, format) { 59 | this.date = moment(dateString, moment().toMomentFormatString(format)).toDate(); 60 | } 61 | 62 | setFirstDayOfWeek() { 63 | this.firstDayOfWeek = 2; 64 | } 65 | } 66 | 67 | module.exports = Calendar; 68 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/helpers/cardsHelper.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* global request */ 3 | 4 | 'use strict'; 5 | 6 | /** 7 | * Maps Stripe card brand like 'visa' to SFCC card type, exactly as defined in 8 | * BM > Merchant Tools > Ordering > Payment Methods > Credit/Debit Cards. 9 | * 10 | * @param {string} stripeCardBrand - card brand as returned by Stripe APIs 11 | * @return {string} - SFCC card type 12 | */ 13 | function getCardTypeByBrand(stripeCardBrand) { 14 | var brandsToCardTypeMap = { 15 | visa: 'Visa', 16 | mastercard: 'Master', 17 | amex: 'Amex', 18 | discover: 'Discover' 19 | }; 20 | 21 | return brandsToCardTypeMap[stripeCardBrand] || stripeCardBrand; 22 | 23 | // The mapping does not need to be exact, as validatePaymentInstruments has 24 | // been modified not to check for a match in case card payment are handled by 25 | // Stripe. So values can be stored as resource strings and the following code 26 | // can be used instead: 27 | // return require('dw/web/Resource').msg('cardtype.' + stripeCardBrand, 'stripe', stripeCardBrand); 28 | } 29 | 30 | /** 31 | * Returns the SFCC card type based on the Stripe-SFCC mappings and the request 32 | * For new cards we receive only card brand and will get the type based on that 33 | * For saved cards we already have the cart type to use 34 | * 35 | * @return {string} - SFCC card type 36 | */ 37 | function getCardType() { 38 | const paramsMap = request.httpParameterMap; 39 | const cardBrand = paramsMap.stripe_card_brand.stringValue; 40 | var cardType = paramsMap.stripe_card_type.stringValue; 41 | 42 | if (!cardType && cardBrand) { 43 | cardType = getCardTypeByBrand(cardBrand); 44 | } 45 | 46 | return cardType; 47 | } 48 | 49 | exports.getCardTypeByBrand = getCardTypeByBrand; 50 | exports.getCardType = getCardType; 51 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/templates/default/checkout/billing/paymentOptions.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 |
19 | 20 | 21 | 23 | 24 | 25 | 28 |
29 |
30 |
31 | 32 |
33 |
34 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/templates/default/components/footer/footer_UI.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | third-party add-ons 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | pageContext = new Object(); 24 | 25 | 26 | 30 | // Stripe changes BEGIN 31 | 32 | // Stripe changes END 33 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/templates/default/checkout/confirmation/confirmation.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | This template visualizes the order confirmation page. Note, that it 8 | uses a different decorator template. 9 | It displays the order related information, such as the order number, 10 | creation date, payment information, order totals and shipments of 11 | the order. 12 | 13 | 14 |
create-account"> 15 |
16 | 17 | Stripe changes BEGIN 18 | 19 | 20 | 21 |

${Resource.msg('stripe.confirmation.thankyou','stripe',null)}

22 | 23 |

${Resource.msg('confirmation.thankyou','checkout',null)}

24 |
25 | 26 | Stripe changes END 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 |
35 | 36 | 37 | 38 | 43 |
44 | 45 |
46 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/templates/default/checkout/billing/paymentOptions/paymentOptionsTabs.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | When Stripe Payment Element is Enabled we hide CREDIT_CARD form when there are No saved cards because credit card template is used to display saved cards only in that case 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/controllers/PaymentInstruments.js: -------------------------------------------------------------------------------- 1 | /* global customer */ 2 | 3 | 'use strict'; 4 | 5 | var server = require('server'); 6 | var page = module.superModule; 7 | server.extend(page); 8 | 9 | var userLoggedIn = require('*/cartridge/scripts/middleware/userLoggedIn'); 10 | 11 | server.append('List', function (req, res, next) { 12 | var stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 13 | if (stripeHelper.isStripeEnabled()) { 14 | var wallet = stripeHelper.getStripeWallet(customer); 15 | var paymentInstruments = wallet.getPaymentInstruments(); 16 | res.setViewData({ 17 | paymentInstruments: paymentInstruments, 18 | noSavedPayments: paymentInstruments.length === 0 19 | }); 20 | } 21 | next(); 22 | }); 23 | 24 | server.prepend('DeletePayment', userLoggedIn.validateLoggedInAjax, function (req, res, next) { 25 | var array = require('*/cartridge/scripts/util/array'); 26 | var stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 27 | var wallet = stripeHelper.getStripeWallet(customer); 28 | 29 | if (!stripeHelper.isStripeEnabled()) { 30 | return next(); 31 | } 32 | 33 | var data = res.getViewData(); 34 | if (data && !data.loggedin) { 35 | res.json(); 36 | this.emit('route:Complete', req, res); 37 | return null; 38 | } 39 | 40 | var UUID = req.querystring.UUID; 41 | var paymentInstruments = req.currentCustomer.wallet.paymentInstruments; 42 | var paymentToDelete = array.find(paymentInstruments, function (item) { 43 | return UUID === item.UUID; 44 | }); 45 | 46 | // Trigger SFRA payment delete if we receive actual payment instrument ID 47 | if (paymentToDelete) { 48 | return next(); 49 | } 50 | 51 | wallet.removePaymentInstrument({ custom: { stripeId: UUID } }); 52 | 53 | res.json({ UUID: UUID }); 54 | this.emit('route:Complete', req, res); 55 | return null; 56 | }); 57 | 58 | module.exports = server.exports(); 59 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/default/order/striperadar.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | var order = pdict.Order; 7 | var riskScore = order.custom.stripeRiskScore ? parseInt(order.custom.stripeRiskScore) : -1; 8 | 9 | 10 | 49 | 50 |
51 |

${Resource.msg('riskscore.radarinsights','stripebm', null)}

52 |

53 | ${Resource.msg('riskscore.riskevaluation','stripebm', null)} 54 |

55 | 56 | ${riskScore.toFixed(0)} ${order.custom.stripeRiskLevel} 57 | 58 | ${riskScore.toFixed(0)} ${order.custom.stripeRiskLevel} 59 | 60 | ${riskScore.toFixed(0)} ${order.custom.stripeRiskLevel} 61 | 62 | ${Resource.msg('riskscore.unknownrisk','stripebm', null)} 63 | 64 |
65 | -------------------------------------------------------------------------------- /metadata/stripe_site_template/jobs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | true 12 | true 13 | false 14 | true 15 | false 16 | 17 | 18 | 19 | 20 | 21 | 22 | 2019-06-21Z 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 2020-02-04Z 43 | 18:50:00.000Z 44 | 5m 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/helpers/paymentprocessors/stripeApmHelper.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* global request */ 3 | 4 | 'use strict'; 5 | 6 | var PaymentMgr = require('dw/order/PaymentMgr'); 7 | var Transaction = require('dw/system/Transaction'); 8 | 9 | /** 10 | * Handle alternative payment 11 | * 12 | * @param {array} args with paramenters 13 | * @returns {array} - array with result info 14 | */ 15 | function Handle(args) { 16 | const checkoutHelper = require('*/cartridge/scripts/stripe/helpers/checkoutHelper'); 17 | const paramsMap = request.httpParameterMap; 18 | const selectedPaymentMethodID = paramsMap.dwfrm_billing_paymentMethods_selectedPaymentMethodID.stringValue || paramsMap.dwfrm_billing_paymentMethod.stringValue;// app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value; 19 | const params = { 20 | sourceId: paramsMap.stripe_source_id.stringValue 21 | }; 22 | 23 | try { 24 | Transaction.begin(); 25 | checkoutHelper.createStripePaymentInstrument(args.Basket, selectedPaymentMethodID, params); 26 | Transaction.commit(); 27 | return { 28 | success: true 29 | }; 30 | } catch (e) { 31 | Transaction.rollback(); 32 | return { 33 | success: false, 34 | error: true, 35 | errorMessage: e.message 36 | }; 37 | } 38 | } 39 | 40 | /** 41 | * Authorize alternative payment 42 | * 43 | * @param {array} args with paramenters 44 | * @returns {Object} - object with result info 45 | */ 46 | function Authorize(args) { 47 | const paymentInstrument = args.PaymentInstrument; 48 | const paymentProcessor = PaymentMgr.getPaymentMethod(paymentInstrument.getPaymentMethod()).getPaymentProcessor(); 49 | 50 | Transaction.wrap(function () { 51 | paymentInstrument.paymentTransaction.paymentProcessor = paymentProcessor; 52 | }); 53 | 54 | return { 55 | authorized: true, 56 | error: false 57 | }; 58 | } 59 | 60 | exports.Handle = Handle; 61 | exports.Authorize = Authorize; 62 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var sgmfScripts = require('sgmf-scripts'); 3 | var ExtractTextPlugin = require('sgmf-scripts')['extract-text-webpack-plugin']; 4 | 5 | module.exports = [{ 6 | mode: 'production', 7 | name: 'js', 8 | entry: sgmfScripts.createJsPath(), 9 | output: { 10 | path: path.resolve('./cartridges/app_stripe_sfra/cartridge/static/'), 11 | filename: '[name].js' 12 | } 13 | }, 14 | { 15 | mode: 'production', 16 | name: 'scss', 17 | entry: sgmfScripts.createScssPath(), 18 | output: { 19 | path: path.resolve('./cartridges/app_stripe_sfra/cartridge/static/'), 20 | filename: '[name].css' 21 | }, 22 | module: { 23 | rules: [{ 24 | test: /\.scss$/, 25 | use: ExtractTextPlugin.extract({ 26 | use: [{ 27 | loader: 'css-loader', 28 | options: { 29 | url: false, 30 | minimize: true, 31 | sourceMap: true 32 | } 33 | }, { 34 | loader: 'postcss-loader', 35 | options: { 36 | plugins: [ 37 | require('autoprefixer')() 38 | ], 39 | sourceMap: true 40 | } 41 | }, { 42 | loader: 'sass-loader', 43 | options: { 44 | includePaths: [ 45 | path.resolve('node_modules'), 46 | path.resolve('node_modules/flag-icon-css/sass') 47 | ], 48 | sourceMap: true 49 | } 50 | }] 51 | }) 52 | }] 53 | }, 54 | resolve: { 55 | alias: { 56 | base: path.resolve(__dirname, '../storefront-reference-architecture/cartridges/app_storefront_base/cartridge/client/default/scss') 57 | } 58 | }, 59 | plugins: [ 60 | new ExtractTextPlugin({ filename: '[name].css' }) 61 | ] 62 | }]; 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "int_stripe_sfra", 3 | "version": "24.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "sgmf-scripts --lint js", 8 | "upload": "sgmf-scripts --upload -- ", 9 | "uploadCartridge": "sgmf-scripts --uploadCartridge int_stripe_core && sgmf-scripts --uploadCartridge int_stripe_sfra && sgmf-scripts --uploadCartridge app_stripe_sfra", 10 | "compile:js": "sgmf-scripts --compile js", 11 | "compile:scss": "sgmf-scripts --compile css", 12 | "watch": "sgmf-scripts --watch", 13 | "watch:static": "sgmf-scripts --watch static", 14 | "test": "mocha tests/unit/**/*.js", 15 | "test:integration": "babel-node node_modules/protractor/bin/protractor protractor-conf.js" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "ISC", 20 | "devDependencies": { 21 | "babel": "^6.5.2", 22 | "babel-cli": "^6.26.0", 23 | "babel-core": "^6.26.3", 24 | "babel-eslint": "^8.0.2", 25 | "babel-preset-latest": "^6.22.0", 26 | "babel-preset-stage-2": "^6.24.1", 27 | "bootstrap": "^5.0.2", 28 | "chai": "^4.3.4", 29 | "chromedriver": "^91.0.1", 30 | "css-loader": "^0.28.11", 31 | "eslint": "^7.30.0", 32 | "eslint-config-airbnb-base": "^14.2.1", 33 | "eslint-plugin-async-await": "^0.0.0", 34 | "eslint-plugin-import": "^2.23.4", 35 | "eslint-plugin-protractor": "^2.1.1", 36 | "husky": "^7.0.1", 37 | "istanbul": "^0.4.4", 38 | "jasmine-spec-reporter": "^7.0.0", 39 | "lint-staged": "^11.0.1", 40 | "mocha": "^9.0.2", 41 | "moment": "^2.29.1", 42 | "moment-jdateformatparser": "^1.2.1", 43 | "node-fetch": "^2.6.1", 44 | "sass": "^1.69.7", 45 | "postcss-loader": "^2.1.5", 46 | "protractor": "^7.0.0", 47 | "proxyquire": "2.1.3", 48 | "sandboxed-module": "^2.0.4", 49 | "sass-loader": "^13.3.2", 50 | "sgmf-scripts": "^2.0.0", 51 | "sinon": "^11.1.1", 52 | "stylelint": "^13.13.1", 53 | "stylelint-config-standard": "^22.0.0", 54 | "stylelint-scss": "^3.19.0" 55 | }, 56 | "packageName": "app_stripe_sfra", 57 | "paths": { 58 | "base": "../storefront-reference-architecture/cartridges/app_storefront_base/" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/templates/default/account/payment/paymentinstrumentlist.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

${Resource.msg('account.paymentinstrumentlist.header','account',null)}

8 | 9 | ${Resource.msg('account.paymentinstrumentlist.addcard','account',null)} 10 | 11 | 12 |
    13 | 14 |
  • first last ${pdict.PaymentInstruments[loopstate.count - 1].creditCardType}"> 15 | 16 |
    17 |
    18 | 21 | 22 |
    23 |
    24 | // Stripe changes BEGIN 25 | 26 | // Stripe changes END 27 |
  • 28 |
    29 |
30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/controllers/Account.js: -------------------------------------------------------------------------------- 1 | /* global customer, request */ 2 | 3 | 'use strict'; 4 | 5 | var server = require('server'); 6 | var page = module.superModule; 7 | server.extend(page); 8 | 9 | server.append('Show', function (req, res, next) { 10 | var stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 11 | if (stripeHelper.isStripeEnabled()) { 12 | var wallet = stripeHelper.getStripeWallet(customer); 13 | var paymentInstruments = wallet.getPaymentInstruments(); 14 | var viewData = res.getViewData(); 15 | if (paymentInstruments && paymentInstruments.length > 0) { 16 | viewData.payment = paymentInstruments[0]; 17 | } 18 | 19 | res.setViewData(viewData); 20 | } 21 | next(); 22 | }); 23 | 24 | server.post('UpdateBillingAddress', function (req, res, next) { 25 | var CustomerMgr = require('dw/customer/CustomerMgr'); 26 | var Transaction = require('dw/system/Transaction'); 27 | var addressHelpers = require('*/cartridge/scripts/helpers/addressHelpers'); 28 | 29 | var customer = CustomerMgr.getCustomerByCustomerNumber( 30 | req.currentCustomer.profile.customerNo 31 | ); 32 | var addressBook = customer.getProfile().getAddressBook(); 33 | if (customer.authenticated) { 34 | var params = request.httpParameterMap; 35 | var newAddress = JSON.parse(params.requestBodyAsString); 36 | Transaction.wrap(function () { 37 | var address = null; 38 | address = newAddress.addressId 39 | ? addressBook.getAddress(newAddress.addressId) 40 | : null; 41 | 42 | if (address) { 43 | if (newAddress.addressId) { 44 | address.setID(newAddress.addressId); 45 | } 46 | 47 | // Save form's address 48 | addressHelpers.updateAddressFields(address, newAddress); 49 | 50 | res.json({ 51 | success: true 52 | }); 53 | } else { 54 | res.json({ 55 | success: false 56 | }); 57 | } 58 | }); 59 | } else { 60 | res.json({ 61 | success: false 62 | }); 63 | } 64 | return next(); 65 | }); 66 | 67 | module.exports = server.exports(); 68 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/resources/stripebm.properties: -------------------------------------------------------------------------------- 1 | 2 | ############################################## 3 | # Stripe BM Translations 4 | ############################################## 5 | 6 | quicksetup.title=Stripe Quick Setup 7 | quicksetup.description=Quickly initialize Stripe configurations by filling your Stripe Public and Private keys and proceed with configuration 8 | quicksetup.sitestoappy=Sites where to Apply Stripe Config 9 | quicksetup.publickey=Publishable key 10 | quicksetup.privatekey=Secret key 11 | quicksetup.setup=Quick Setup 12 | quicksetup.note=Please Note: If there are any pre-existing Stripe configurations, they will be overridden by Stripe Quick setup. 13 | quicksetup.nositeselectederrmsg=Please Select at least one site ID to proceed with Stripe Quick Setup 14 | 15 | paymentssetup.title=Stripe Payment Methods Setup 16 | paymentssetup.info=Generates a payment-methods.xml based on selected Stripe payment methods. Then you can import it in Merchant Tools > Ordering > Import & Export -> Payment Methods. 17 | payemntssetup.submit=Generate XML File 18 | 19 | paymentsrefund.title=Stripe Payments Refund 20 | paymentsrefund.info=Issue a refund by entering Order number and amount to be refunded 21 | paymentsrefund.ordernumber=Order Number 22 | paymentsrefund.amounttorefund=Amount to Refund 23 | paymentsrefund.issuerefund=Issue Refund 24 | paymentsrefund.ordernotfound=Order with ID {0} NOT Found. 25 | paymentsrefund.refundsucceeded=Refund Succeeded 26 | paymentsrefund.refundpending=Refund Status Pending (The bank is processing this payment's refund) 27 | paymentsrefund.cannotrefundorder=Cannot refund order because missing payment intent id and source id 28 | 29 | 30 | paymentscapture.title=Stripe Payments Capture 31 | paymentscapture.info=Issue a Capture by entering Order number and amount to be captured 32 | paymentscapture.ordernumber=Order Number 33 | paymentscapture.amounttocapture=Amount to Capture 34 | paymentscapture.issuecapture=Issue Capture 35 | paymentscapture.capturesucceeded=Capture Succeeded 36 | paymentscapture.capturepending=Capture Status Pending (The bank is processing this payment's capture) 37 | paymentscapture.cannotcaptureorder=Cannot capture order because missing payment intent id and source id 38 | 39 | riskscore.viewonstripe=View on Stripe Dashboard 40 | riskscore.riskevaluation=Risk evaluation 41 | riskscore.radarinsights=Stripe Radar insights 42 | riskscore.unknownrisk=Unknown risk 43 | -------------------------------------------------------------------------------- /protractor-conf.js: -------------------------------------------------------------------------------- 1 | 2 | import { SpecReporter } from 'jasmine-spec-reporter'; 3 | 4 | export const config = { 5 | framework: 'jasmine2', 6 | specs: ['tests/integration/specs/**/*Spec.js'], 7 | SELENIUM_PROMISE_MANAGER: false, 8 | directConnect: true, 9 | chromeDriver: 'node_modules/chromedriver/lib/chromedriver/chromedriver.exe', 10 | jasmineNodeOpts: { 11 | // remove ugly protractor dot reporter 12 | // print: () => { }, 13 | }, 14 | baseUrl: 'https://hostname.demandware.net/on/demandware.store/Sites-RefArch-Site/en_US/', 15 | onPrepare: () => { 16 | /** 17 | * If you are testing against a non-angular site - set ignoreSynchronization setting to true 18 | * 19 | * If true, Protractor will not attempt to synchronize with the page before 20 | * performing actions. This can be harmful because Protractor will not wait 21 | * until $timeouts and $http calls have been processed, which can cause 22 | * tests to become flaky. This should be used only when necessary, such as 23 | * when a page continuously polls an API using $timeout. 24 | * 25 | * @type {boolean} 26 | */ 27 | browser.ignoreSynchronization = true; 28 | // browser.waitForAngularEnabled(false); 29 | 30 | jasmine.getEnv().addReporter(new SpecReporter({ 31 | // Defaults: https://github.com/bcaudan/jasmine-spec-reporter#default-options 32 | // Configuration: https://github.com/bcaudan/jasmine-spec-reporter/blob/master/src/configuration.ts 33 | suite: { 34 | displayNumber: true // display each suite number (hierarchical) 35 | }, 36 | spec: { 37 | displaySuccessful: true, 38 | displayPending: true, // display each pending spec 39 | displayDuration: true // display each spec duration 40 | }, 41 | summary: { 42 | displaySuccessful: false, // display summary of all successes after execution 43 | displayFailed: true, // display summary of all failures after execution 44 | displayPending: false, // display summary of all pending specs after execution 45 | displayDuration: true 46 | } 47 | })); 48 | }, 49 | capabilities: { 50 | browserName: 'chrome', 51 | platform: 'ANY', 52 | chromeOptions: { 53 | args: ['show-fps-counter=true'] 54 | } 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/checkout/billing/altpaymentmethods.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Stripe Payment Elements 27 | ------------------- 28 | 29 | 30 |
31 |
37 |
38 |
39 |
-------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/templates/default/stripebm/quicksetup.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

${Resource.msg('quicksetup.title','stripebm', null)}

6 |

${Resource.msg('quicksetup.description','stripebm', null)}

7 | 8 |
10 | 11 | 12 | 13 | 16 | 25 | 26 | 27 | 30 | 33 | 34 | 35 | 38 | 41 | 42 | 43 | 46 | 47 | 48 | 51 | 52 | 53 | 56 | 58 | 59 | 60 |
14 | ${Resource.msg('quicksetup.sitestoappy','stripebm', null)} 15 | 17 | 18 | 19 | 21 |
22 |
23 | 24 |
28 | ${Resource.msg('quicksetup.publickey','stripebm', null)} 29 | 31 | 32 |
36 | ${Resource.msg('quicksetup.privatekey','stripebm', null)} 37 | 39 | 40 |
44 | ${Resource.msg('quicksetup.note','stripebm', null)} 45 |
49 | 50 |
54 | 55 | 57 |
61 |
62 | 63 | 64 |
-------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/models/payment.js: -------------------------------------------------------------------------------- 1 | /* global request, dw */ 2 | /* eslint-disable no-plusplus */ 3 | 4 | 'use strict'; 5 | 6 | var PaymentMgr = require('dw/order/PaymentMgr'); 7 | var collections = require('*/cartridge/scripts/util/collections'); 8 | var base = module.superModule; 9 | 10 | /** 11 | * Creates an array of objects containing applicable payment methods 12 | * @param {dw.util.ArrayList} paymentMethods - An ArrayList of 13 | * applicable payment methods that the user could use for the current basket. 14 | * @returns {Array} of object that contain information about the applicable payment methods for the 15 | * current cart 16 | */ 17 | function applicablePaymentMethods(paymentMethods) { 18 | return collections.map(paymentMethods, function (method) { 19 | return { 20 | ID: method.ID, 21 | name: method.name 22 | }; 23 | }); 24 | } 25 | 26 | /** 27 | * Payment class that represents payment information for the current basket 28 | * @param {dw.order.Basket} currentBasket - the target Basket object 29 | * @param {dw.customer.Customer} currentCustomer - the associated Customer object 30 | * @param {string} countryCode - the associated Site countryCode 31 | * @constructor 32 | */ 33 | function Payment(currentBasket, currentCustomer, countryCode) { 34 | base.call(this, currentBasket, currentCustomer, countryCode); 35 | 36 | var stripeHelper = require('*/cartridge/scripts/stripe/helpers/stripeHelper'); 37 | if (stripeHelper.isStripeEnabled()) { 38 | var paymentAmount = currentBasket.totalGrossPrice; 39 | var paymentMethods = PaymentMgr.getApplicablePaymentMethods( 40 | currentCustomer, 41 | countryCode, 42 | paymentAmount.value 43 | ); 44 | 45 | paymentMethods = stripeHelper.getStripePaymentMethods(paymentMethods, request.locale); 46 | 47 | this.applicablePaymentMethods = paymentMethods ? applicablePaymentMethods(paymentMethods) : null; 48 | } else { 49 | var applicablePaymentMethodsWithoutStripe = []; 50 | for (var i = 0; i < this.applicablePaymentMethods.length; i++) { 51 | var paymentMethod = this.applicablePaymentMethods[i]; 52 | var currentPaymentMethod = dw.order.PaymentMgr.getPaymentMethod(paymentMethod.ID); 53 | if (currentPaymentMethod.paymentProcessor.ID !== 'STRIPE_APM' && currentPaymentMethod.paymentProcessor.ID !== 'STRIPE_CREDIT') { 54 | applicablePaymentMethodsWithoutStripe.push({ 55 | ID: paymentMethod.ID, 56 | name: paymentMethod.name 57 | }); 58 | } 59 | } 60 | 61 | this.applicablePaymentMethods = applicablePaymentMethodsWithoutStripe; 62 | } 63 | } 64 | 65 | module.exports = Payment; 66 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/jobs/deleteCustomObjectsStep.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* eslint-disable no-plusplus */ 3 | 4 | 'use strict'; 5 | 6 | /** 7 | * Composes a search query string based on job parameters. 8 | * 9 | * @param {Object} jobParams - Job parameters 10 | * @return {string} - Generted query string 11 | */ 12 | function getSearchQueryString(jobParams) { 13 | var clauses = []; 14 | 15 | if (jobParams.UNKNOWN) { 16 | clauses.push("custom.processingStatus='UNKNOWN'"); 17 | } 18 | if (jobParams.PENDING_CHARGE) { 19 | clauses.push("custom.processingStatus='PENDING_CHARGE'"); 20 | } 21 | if (jobParams.PROCESS) { 22 | clauses.push("custom.processingStatus='PROCESS'"); 23 | } 24 | if (jobParams.PROCESSED) { 25 | clauses.push("custom.processingStatus='PROCESSED'"); 26 | } 27 | if (jobParams.FAIL_OR_CANCEL) { 28 | clauses.push("custom.processingStatus='FAIL_OR_CANCEL'"); 29 | } 30 | 31 | return clauses.length 32 | ? clauses.join(' OR ') 33 | : null; 34 | } 35 | 36 | exports.execute = function (jobParams) { 37 | const Status = require('dw/system/Status'); 38 | const Logger = require('dw/system/Logger'); 39 | 40 | const queryString = getSearchQueryString(jobParams); 41 | 42 | if (!queryString) { // No statuses selected 43 | Logger.info('No statuses selected, exiting'); 44 | return new Status(Status.OK); 45 | } 46 | 47 | const CustomObjectMgr = require('dw/object/CustomObjectMgr'); 48 | const Transaction = require('dw/system/Transaction'); 49 | const webhooksHelper = require('*/cartridge/scripts/stripe/helpers/webhooksHelper'); 50 | 51 | var stripeObjectsIter; 52 | try { 53 | const customObjectType = webhooksHelper.getNotificationsCustomObjectType(); 54 | stripeObjectsIter = CustomObjectMgr.queryCustomObjects(customObjectType, queryString, null); 55 | let objectsRemovedCount = 0; 56 | 57 | while (stripeObjectsIter.hasNext()) { 58 | var stripeNoficationObject = stripeObjectsIter.next(); 59 | 60 | Transaction.wrap(function () { // eslint-disable-line 61 | CustomObjectMgr.remove(stripeNoficationObject); 62 | objectsRemovedCount++; 63 | }); 64 | } 65 | 66 | Logger.info('Removed {0} custom objects.', objectsRemovedCount); 67 | } catch (e) { 68 | var errMsg = e.message; 69 | Logger.error(errMsg); 70 | return new Status(Status.ERROR); 71 | } finally { 72 | if (stripeObjectsIter) { 73 | try { 74 | stripeObjectsIter.close(); 75 | } catch (e) { 76 | Logger.error('Failed to close seekable iterator.'); 77 | } 78 | } 79 | } 80 | 81 | return new Status(Status.OK); 82 | }; 83 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/templates/default/checkout/expressCheckout/stripeMinicartExpressCheckoutButton.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | var assets = require('*/cartridge/scripts/assets.js'); 26 | assets.addJs('/js/stripe.expressCheckout.js'); 27 | 28 | 29 |
30 | 31 |
-------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/templates/default/checkout/confirmation/confirmation.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | var assets = require('*/cartridge/scripts/assets.js'); 4 | assets.addCss('/css/checkout/checkout.css'); 5 | assets.addJs('/js/checkoutRegistration.js'); 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

${Resource.msg('title.thank.you.page','confirmation',null)}

14 |
15 |
1}">multi-ship"> 16 |
17 |
18 | 19 | 20 | Stripe changes BEGIN 21 | 22 | 23 |

${Resource.msg('stripe.confirmation.thankyou','stripe',null)}

24 |

${Resource.msg('stripe.confirmation.shippingnote', 'stripe', null)}

25 | 26 |

${Resource.msg('msg.placed.order.thank.you','confirmation',null)}

27 |

28 |
29 | 30 | Stripe changes END 31 | 32 |
33 |
34 |
35 | 36 |
37 | 38 |
39 |
40 | 48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/models/customerPaymentInstrument.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | 3 | 'use strict'; 4 | 5 | /** 6 | * An adapter for Stripe payment instrument data objects, to make then 7 | * compatible (have the same properties) as dw.order.PaymentInstrumnent. 8 | * 9 | * @param {Object} stripePaymentInstrumentData - Payment instrument obejct 10 | * received from Stripe service 11 | * @param {boolean} isDefault - Indicates whether this is the default payment 12 | * instrument of the customer 13 | */ 14 | function CustomerPaymentInstrument(stripePaymentInstrumentData, isDefault) { 15 | const stripeCardData = stripePaymentInstrumentData.card; 16 | const billingDetails = stripePaymentInstrumentData.billing_details; 17 | 18 | const maskedCardNumber = '************' + stripeCardData.last4; 19 | 20 | var custom = Object.create(null, { 21 | stripeId: { 22 | value: stripePaymentInstrumentData.id, 23 | enumerable: true 24 | }, 25 | stripeObject: { 26 | value: stripePaymentInstrumentData.object, 27 | enumerable: true 28 | }, 29 | stripeType: { 30 | value: stripePaymentInstrumentData.type, 31 | enumerable: true 32 | }, 33 | stripeCardBrand: { 34 | value: stripeCardData.brand, 35 | enumerable: true 36 | }, 37 | isDefault: { 38 | value: isDefault || false, 39 | enumerable: true 40 | } 41 | }); 42 | 43 | Object.defineProperties(this, { 44 | UUID: { 45 | value: stripePaymentInstrumentData.id, 46 | enumerable: true 47 | }, 48 | creditCardNumber: { 49 | value: maskedCardNumber, 50 | enumerable: true 51 | }, 52 | maskedCreditCardNumber: { 53 | value: maskedCardNumber, 54 | enumerable: true 55 | }, 56 | permanentlyMasked: { 57 | value: true, 58 | enumerable: true 59 | }, 60 | creditCardType: { 61 | value: require('../helpers/cardsHelper').getCardTypeByBrand(stripeCardData.brand), 62 | enumerable: true 63 | }, 64 | creditCardHolder: { 65 | value: (billingDetails && billingDetails.name) || ' ', 66 | enumerable: true 67 | }, 68 | creditCardExpirationYear: { 69 | value: stripeCardData.exp_year, 70 | enumerable: true 71 | }, 72 | creditCardExpirationMonth: { 73 | value: stripeCardData.exp_month, 74 | enumerable: true 75 | }, 76 | creditCardNumberLastDigits: { 77 | value: stripeCardData.last4, 78 | enumerable: true 79 | }, 80 | custom: { 81 | value: Object.freeze(custom), 82 | enumerable: true 83 | } 84 | }); 85 | } 86 | 87 | module.exports = CustomerPaymentInstrument; 88 | -------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/cartridge/controllers/RedirectURL.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 'use strict'; 3 | 4 | /** 5 | * Controller that handles URL redirects. 6 | * 7 | * It is called by the system to handle URL mappings (static mappings and mapping rules). 8 | * The mappings are configured in Business Manager. This controller is highly performance critical, 9 | * because it is frequently called in case of exploit scans. 10 | * 11 | * Please follow these rules: 12 | * - no or only a few database calls 13 | * - simple (static) template response 14 | * - caching the result page is a must 15 | * 16 | * @module controllers/RedirectURL 17 | */ 18 | 19 | /* API Includes */ 20 | var URLRedirectMgr = require('dw/web/URLRedirectMgr'); 21 | 22 | /* Script Modules */ 23 | var app = require('*/cartridge/scripts/app'); 24 | var guard = require('*/cartridge/scripts/guard'); 25 | 26 | /** 27 | * Gets the redirect. Renders the template for a redirect (util/redirectpermanent template). If no redirect can be found, 28 | * renders an error page (util/redirecterrorutil/redirecterror template). 29 | */ 30 | function start() { 31 | 32 | //Stripe change BEGIN 33 | if (URLRedirectMgr.getRedirectOrigin() === '/.well-known/apple-developer-merchantid-domain-association') { // Intercept the incoming path request 34 | app.getView().render('stripe/util/apple'); 35 | return; 36 | } 37 | //Stripe change END 38 | 39 | var redirect = URLRedirectMgr.getRedirect(), 40 | location = redirect ? redirect.getLocation() : null; 41 | 42 | if (!location) { 43 | response.setStatus(410); 44 | app.getView().render('util/redirecterror'); 45 | } else { 46 | app.getView({ 47 | Location: location 48 | }).render('util/redirectpermanent'); 49 | } 50 | } 51 | 52 | /** 53 | * Hostname-only URLs (http://sitegenesis.com/) cannot be redirected using the URL mapping framework. 54 | * Instead, specify this controller in your site's hostname alias in Business Manager. 55 | * 56 | * However, a redirect to the homepage is performed by the 57 | * Default controller Start function. 58 | * The hostname in the URL is the site's HTTP Hostname, if one is configured in Business Manager. 59 | * Also, you can provide a URL to redirect to an optional parameter, Location. 60 | * 61 | * @example 62 | * Redirect http[s]://sitegenesis.com/ to http://www.sitegenesis.com/: 63 | * sitegenesis.com,,RedirectURL-Hostname,Location,http://www.sitegenesis.com/ 64 | */ 65 | function hostName() { 66 | var Redirect = require('*/cartridge/scripts/util/Redirect'); 67 | app.getView({ 68 | Location: Redirect.validateURL(request.httpParameterMap.Location.stringValue) 69 | }).render('util/redirectpermanent'); 70 | } 71 | 72 | 73 | /* 74 | * Module exports 75 | */ 76 | 77 | /* 78 | * Web exposed methods 79 | */ 80 | /** Gets a redirect and renders it. 81 | * @see module:controllers/RedirectURL~start */ 82 | exports.Start = guard.ensure([], start); 83 | /** Used by the platform for URL redirects. 84 | * @see module:controllers/RedirectURL~hostName */ 85 | exports.Hostname = guard.ensure([], hostName); 86 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/templates/default/account/payment/paymentForm.isml: -------------------------------------------------------------------------------- 1 | 2 | var assets = require('*/cartridge/scripts/assets.js'); 3 | assets.addJs('/js/stripe.newcardform.js'); 4 | 5 | 6 |
13 | 14 | 15 |
required"> 17 | 20 | 22 | autocomplete="cc-name"> 23 |
24 | 25 |
26 |
27 | 28 | 29 | 30 |
required"> 32 | 33 | 34 | 35 | 38 |
39 |
40 |
41 |
42 |
43 | 44 | 45 | 46 |
47 |
48 | 49 | 52 |
53 |
54 | 55 | 56 | 57 | 58 |
59 | 62 |
63 | 64 |
65 |
66 | 67 |
68 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/footerinclude.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/mail/orderfailed.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The "subject" tag contains the mail subject and can contain dynamic information, like the order number. 4 | ${pdict.MailSubject} 5 | 6 | The "to" tag contains the email address of the recipient, the "from" tag the email address of the sender. 7 | Each tag is to be specified max. once. Multiple email address can be separated by "," (see RFC2822). 8 | 9 | ${pdict.Order.customerEmail} 10 | ${dw.system.Site.getCurrent().getCustomPreferenceValue('customerServiceEmail')} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 57 | 58 |
20 |
21 | 22 | 23 | 28 | 35 | 36 | 37 | 53 | 54 |
24 | 25 | ${Resource.msg('global.storename','locale',null)} 26 | 27 | 29 | ${Resource.msg('global.storename','locale',null)}
30 | ${Resource.msg('order.orderconfirmation-email.storeaddress','order',null)}
31 | ${Resource.msg('order.orderconfirmation-email.storelocation','order',null)}
32 | ${Resource.msg('global.storenameurl','locale',null)}
33 | ${Resource.msg('order.orderconfirmation-email.storephone','order',null)} 34 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 50 |
${Resource.msg('confirmation.thankyou','checkout',null)}
45 |

${Resource.msg('order.orderfailed-email.001','stripe',null)}

46 |

${Resource.msg('confirmation.message','checkout',null)}

47 |

${Resource.msg('confirmation.contact','checkout',null)}

48 |
51 | 52 |
55 |
56 |
59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /tests/mocks/dw-mocks/dw/system/Site.js: -------------------------------------------------------------------------------- 1 | const Calendar = require('../util/Calendar'); 2 | const Collection = require('../util/dw.util.Collection'); 3 | 4 | let mockPreferences = { 5 | stripeApiKey: 'stripeApiKey' 6 | }; 7 | const preferences = { 8 | smart: true, 9 | galleryDisplay: true, 10 | twilioNumber: '+987654321' 11 | }; 12 | 13 | const Site = { 14 | current: { 15 | getID() { 16 | return 'TestBrand-TestSite'; 17 | }, 18 | getAllowedLocales() { 19 | return new Collection(['en_GB']); 20 | }, 21 | getAllowedCurrencies() { 22 | return new Collection(['GBP']); 23 | }, 24 | getCustomPreferenceValue(key) { 25 | if (Object.prototype.hasOwnProperty.call(mockPreferences, key)) { 26 | return mockPreferences[key]; 27 | } 28 | 29 | return preferences[key]; 30 | }, 31 | getCalendar() { 32 | return new Calendar(); 33 | }, 34 | getPreferences() { 35 | return { 36 | getCustom() { 37 | return preferences; 38 | } 39 | }; 40 | } 41 | }, 42 | getAllSites() { 43 | return new Collection([this.getCurrent()].concat([ 44 | { 45 | getID() { 46 | return 'TestBrand-TestSite2'; 47 | }, 48 | getAllowedLocales() { 49 | return new Collection(['en_AU']); 50 | }, 51 | getAllowedCurrencies() { 52 | return new Collection(['AUD']); 53 | } 54 | }, 55 | { 56 | getID() { 57 | return 'TestBrand2-TestSite'; 58 | }, 59 | getAllowedLocales() { 60 | return new Collection(['en_GB']); 61 | }, 62 | getAllowedCurrencies() { 63 | return new Collection(['GBP']); 64 | } 65 | }, 66 | { 67 | getID() { 68 | return 'TestBrand2-TestSite2'; 69 | }, 70 | getAllowedLocales() { 71 | return new Collection(['en_AU']); 72 | }, 73 | getAllowedCurrencies() { 74 | return new Collection(['AUD']); 75 | } 76 | } 77 | ])); 78 | }, 79 | getCurrent() { 80 | return this.current; 81 | } 82 | }; 83 | 84 | const setMockPreferenceValue = (key, value, isEnum) => { 85 | if (isEnum) { 86 | mockPreferences[key] = { 87 | getValue() { 88 | return value; 89 | } 90 | }; 91 | } else { 92 | mockPreferences[key] = value; 93 | } 94 | }; 95 | 96 | const restore = () => { 97 | mockPreferences = {}; 98 | }; 99 | 100 | module.exports = Site; 101 | module.exports.setMockPreferenceValue = setMockPreferenceValue; 102 | module.exports.restore = restore; 103 | 104 | Object.defineProperty(module.exports, 'preferences', { 105 | get: () => Object.assign({}, preferences, mockPreferences) 106 | }); 107 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/mail/orderreceived.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The "subject" tag contains the mail subject and can contain dynamic information, like the order number. 4 | ${pdict.MailSubject} 5 | 6 | The "to" tag contains the email address of the recipient, the "from" tag the email address of the sender. 7 | Each tag is to be specified max. once. Multiple email address can be separated by "," (see RFC2822). 8 | 9 | ${pdict.Order.customerEmail} 10 | ${dw.system.Site.getCurrent().getCustomPreferenceValue('customerServiceEmail')} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 63 | 64 |
21 |
22 | 23 | 24 | 29 | 36 | 37 | 38 | 54 | 55 | 56 | 59 | 60 |
25 | 26 | ${Resource.msg('global.storename','locale',null)} 27 | 28 | 30 | ${Resource.msg('global.storename','locale',null)}
31 | ${Resource.msg('order.orderconfirmation-email.storeaddress','order',null)}
32 | ${Resource.msg('order.orderconfirmation-email.storelocation','order',null)}
33 | ${Resource.msg('global.storenameurl','locale',null)}
34 | ${Resource.msg('order.orderconfirmation-email.storephone','order',null)} 35 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 |
${Resource.msg('confirmation.thankyou','checkout',null)}
46 |

${Resource.msg('order.ordercreceived-email.001','stripe',null)}

47 |

${Resource.msg('confirmation.message','checkout',null)}

48 |

${Resource.msg('confirmation.contact','checkout',null)}

49 |
52 | 53 |
57 | 58 |
61 |
62 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/templates/default/account/payment/paymentinstrumentdetails.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

${Resource.msg('account.paymentinstrumentlist.addcard', 'account', null)}

12 |
${Resource.msg('global.requiredfield', 'locale', null)}
13 | // Stripe changes BEGIN 14 | 15 | 16 | 17 | // Stripe changes END 18 |
19 |
20 | 21 | var ownerAttributes = { 22 | maxlength: 40, 23 | size: 40 24 | }; 25 | var numberAttributes = { 26 | maxlength: 16, 27 | size: 17 28 | }; 29 | 30 | 31 | 32 | 33 |
34 | 35 | ${Resource.msg('account.paymentinstrumentdetails.expires','account',null)} 36 |
37 | 38 | var currentCountry = require('*/cartridge/scripts/util/Countries').getCurrent(pdict); 39 | 40 | 41 | 42 | 43 |
44 | 47 | 50 |
51 | 52 |
53 |
54 |
55 | 56 | 60 |
61 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/scripts/hooks/payment/processor/stripe_apm.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable new-cap */ 2 | /* eslint-disable no-unused-vars */ 3 | /* global dw */ 4 | // v1 5 | 6 | 'use strict'; 7 | 8 | var collections = require('*/cartridge/scripts/util/collections'); 9 | 10 | var PaymentInstrument = require('dw/order/PaymentInstrument'); 11 | var PaymentMgr = require('dw/order/PaymentMgr'); 12 | var PaymentStatusCodes = require('dw/order/PaymentStatusCodes'); 13 | var Resource = require('dw/web/Resource'); 14 | var Transaction = require('dw/system/Transaction'); 15 | 16 | var StripeAPMHelper = require('*/cartridge/scripts/stripe/helpers/paymentprocessors/stripeApmHelper'); 17 | 18 | /** 19 | * Verifies that entered credit card information is a valid card. If the information is valid a 20 | * credit card payment instrument is created 21 | * @param {dw.order.Basket} basket Current users's basket 22 | * @param {Object} paymentInformation - the payment information 23 | * @return {Object} returns an error object 24 | */ 25 | function Handle(basket, paymentInformation) { 26 | var serverErrors = []; 27 | 28 | var result = StripeAPMHelper.Handle({ Basket: basket }); 29 | if (result.errorMessage) { 30 | serverErrors.push(result.errorMessage); 31 | } 32 | return { fieldErrors: [], serverErrors: serverErrors, error: result.error }; 33 | } 34 | 35 | /** 36 | * Authorizes a payment using a credit card. Customizations may use other processors and custom 37 | * logic to authorize credit card payment. 38 | * @param {number} orderNumber - The current order's number 39 | * @param {dw.order.PaymentInstrument} paymentInstrument - The payment instrument to authorize 40 | * @param {dw.order.PaymentProcessor} paymentProcessor - The payment processor of the current 41 | * payment method 42 | * @return {Object} returns an error object 43 | */ 44 | function Authorize(orderNumber, paymentInstrument, paymentProcessor) { 45 | var serverErrors = []; 46 | var order = dw.order.OrderMgr.getOrder(orderNumber); 47 | 48 | var result = StripeAPMHelper.Authorize({ Order: order, OrderNo: orderNumber, PaymentInstrument: paymentInstrument }); 49 | 50 | if (result.errorMessage) { 51 | serverErrors.push(result.errorMessage); 52 | } 53 | return { fieldErrors: [], serverErrors: serverErrors, error: result.error }; 54 | } 55 | 56 | exports.Handle = Handle; 57 | exports.Authorize = Authorize; 58 | 59 | /** 60 | * Verifies the required information for billing form is provided. 61 | * @param {Object} req - The request object 62 | * @param {Object} paymentForm - the payment form 63 | * @param {Object} viewFormData - object contains billing form data 64 | * @returns {Object} an object that has error information or payment information 65 | */ 66 | function processForm(req, paymentForm, viewFormData) { 67 | var viewData = viewFormData; 68 | 69 | viewData.paymentMethod = { 70 | value: paymentForm.paymentMethod.value, 71 | htmlName: paymentForm.paymentMethod.value 72 | }; 73 | 74 | return { 75 | error: false, 76 | viewData: viewData 77 | }; 78 | } 79 | 80 | /** 81 | * Save the credit card information to login account if save card option is selected 82 | * @param {Object} req - The request object 83 | * @param {dw.order.Basket} basket - The current basket 84 | * @param {Object} billingData - payment information 85 | */ 86 | function savePaymentInformation(req, basket, billingData) { 87 | 88 | } 89 | 90 | exports.processForm = processForm; 91 | exports.savePaymentInformation = savePaymentInformation; 92 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/templates/default/checkout/expressCheckout/stripeExpressCheckoutButton.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 | 38 |
39 |
-------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/scripts/helpers/stripeBmHelper.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* eslint-disable no-plusplus */ 3 | 4 | 'use strict'; 5 | 6 | var PaymentMgr = require('dw/order/PaymentMgr'); 7 | 8 | /** 9 | * Gets the Stripe secret API key from Site Preferences. 10 | * 11 | * @returns {string} Stripe secret API key. 12 | */ 13 | exports.getApiKey = function () { 14 | return require('dw/system/Site').current.getCustomPreferenceValue('stripeApiKey'); 15 | }; 16 | 17 | /** 18 | * Get Stripe Payment Method Definitions 19 | * 20 | * @return {Array} array with Stripe payment methods definitions 21 | */ 22 | function getStripePaymentMethodDefinitions() { 23 | return [ 24 | { 25 | id: 'CREDIT_CARD', 26 | name: 'Stripe Card', 27 | currencies: {}, 28 | payment_processor: 'STRIPE_CREDIT' 29 | }, 30 | { 31 | id: 'STRIPE_PAYMENT_ELEMENT', 32 | name: 'Stripe Payment Element', 33 | currencies: {}, 34 | payment_processor: 'STRIPE_APM' 35 | } 36 | ]; 37 | } 38 | 39 | exports.getStripePaymentMethodDefinitions = getStripePaymentMethodDefinitions; 40 | 41 | /** 42 | * Get Array with Stripe Payment Methods info for Current site 43 | * 44 | * @return {Array} list with Stripe Payment methods info 45 | */ 46 | exports.getStripePaymentMethods = function () { 47 | var result = []; 48 | 49 | var stripePaymentMethods = getStripePaymentMethodDefinitions(); 50 | 51 | for (var i = 0; i < stripePaymentMethods.length; i++) { 52 | var paymentMethodId = stripePaymentMethods[i].id; 53 | var paymentMethodName = stripePaymentMethods[i].name; 54 | var paymentProcessorId = stripePaymentMethods[i].payment_processor; 55 | 56 | var paymentMethod = PaymentMgr.getPaymentMethod(paymentMethodId); 57 | 58 | var isActive = paymentMethod && paymentMethod.isActive() 59 | && (paymentProcessorId === paymentMethod.getPaymentProcessor().getID()); 60 | 61 | result.push({ 62 | id: paymentMethodId, 63 | name: paymentMethodName, 64 | isactive: isActive 65 | }); 66 | } 67 | return result; 68 | }; 69 | 70 | /** 71 | * Writes payment method elements to XML file. 72 | * 73 | * @param {dw.io.XMLStreamWriter} xsw Class used to write XML to file. 74 | * @param {Object} paymentMethod Object containing payment method info. 75 | * @param {Bolean} isEnabled true if payment method needs to be enabled 76 | */ 77 | exports.writePaymentMethod = function (xsw, paymentMethod, isEnabled) { 78 | /* eslint-disable indent */ 79 | xsw.writeStartElement('payment-method'); 80 | xsw.writeAttribute('method-id', paymentMethod.id); 81 | xsw.writeStartElement('name'); 82 | xsw.writeAttribute('xml:lang', 'x-default'); 83 | xsw.writeCharacters(paymentMethod.name); 84 | xsw.writeEndElement(); 85 | 86 | xsw.writeStartElement('enabled-flag'); 87 | xsw.writeCharacters(isEnabled); 88 | xsw.writeEndElement(); 89 | 90 | xsw.writeStartElement('processor-id'); 91 | xsw.writeCharacters(paymentMethod.payment_processor); 92 | xsw.writeEndElement(); 93 | 94 | xsw.writeStartElement('currencies'); 95 | for (var i = 0; i < paymentMethod.currencies.length; i++) { 96 | var currency = paymentMethod.currencies[i]; 97 | 98 | xsw.writeStartElement('currency'); 99 | xsw.writeCharacters(currency); 100 | xsw.writeEndElement(); 101 | } 102 | xsw.writeEndElement(); 103 | 104 | xsw.writeEndElement(); 105 | /* eslint-enable indent */ 106 | }; 107 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/templates/default/checkout/expressCheckout/stripeExpressCheckoutButton.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | var assets = require('*/cartridge/scripts/assets.js'); 27 | assets.addJs('/js/stripe.expressCheckout.js'); 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 |
43 |
-------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/cartridge/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | "dw": true, 8 | "customer": true, 9 | "session": true, 10 | "request": true, 11 | "response": true, 12 | "empty": true, 13 | "PIPELET_ERROR": true, 14 | "PIPELET_NEXT": true 15 | }, 16 | "extends": "eslint:recommended", 17 | "rules": { 18 | "arrow-body-style": 2, 19 | "arrow-parens": 2, 20 | "arrow-spacing": 2, 21 | "block-scoped-var": 0, 22 | "block-spacing": [ 23 | 2, 24 | "never" 25 | ], 26 | "brace-style": 0, 27 | "callback-return": 0, 28 | "camelcase": [ 29 | 2, 30 | { 31 | "properties": "never" 32 | } 33 | ], 34 | "comma-style": [ 35 | 2, 36 | "last" 37 | ], 38 | "computed-property-spacing": [ 39 | 2, 40 | "never" 41 | ], 42 | "consistent-this": 0, 43 | "default-case": 0, 44 | "dot-location": [ 45 | 2, 46 | "property" 47 | ], 48 | "dot-notation": [ 49 | 2, 50 | { 51 | "allowKeywords": true 52 | } 53 | ], 54 | "eol-last": 0, 55 | "func-style": 0, 56 | "global-require": 0, 57 | "guard-for-in": 0, 58 | "handle-callback-err": 2, 59 | "indent": [2, 4, { "SwitchCase": 1 }], 60 | "key-spacing": 2, 61 | "keyword-spacing": [ 62 | 2, 63 | { 64 | "after": true, 65 | "before": true 66 | } 67 | ], 68 | "linebreak-style": [ 69 | 2, 70 | "unix" 71 | ], 72 | "max-depth": 0, 73 | "max-params": 0, 74 | "no-alert": 2, 75 | "no-bitwise": 0, 76 | "no-caller": 0, 77 | "no-continue": 0, 78 | "no-extra-bind": 2, 79 | "no-inner-declarations": [ 80 | 2, 81 | "functions" 82 | ], 83 | "no-invalid-this": 0, 84 | "no-lonely-if": 0, 85 | "no-multi-str": 2, 86 | "no-multiple-empty-lines": 0, 87 | "no-new": 0, 88 | "no-plusplus": 0, 89 | "no-process-env": 2, 90 | "no-return-assign": 2, 91 | "no-spaced-func": 2, 92 | // "no-unmodified-loop-condition": 2, 93 | "no-unneeded-ternary": [ 94 | 2, 95 | { 96 | "defaultAssignment": true 97 | } 98 | ], 99 | "no-unused-expressions": 2, 100 | "no-useless-call": 0, 101 | "object-curly-spacing": [ 102 | 2, 103 | "never" 104 | ], 105 | "operator-assignment": [ 106 | 2, 107 | "always" 108 | ], 109 | "prefer-const": 0, 110 | "prefer-spread": 0, 111 | "quotes": [ 112 | 2, 113 | "single" 114 | ], 115 | "radix": [ 116 | 2, 117 | "as-needed" 118 | ], 119 | "semi-spacing": [ 120 | 2, 121 | { 122 | "after": true, 123 | "before": false 124 | } 125 | ], 126 | // "sort-imports": 2, 127 | "sort-vars": 0, 128 | "space-in-parens": [ 129 | 2, 130 | "never" 131 | ], 132 | "space-unary-ops": [ 133 | 2, 134 | { 135 | "nonwords": false, 136 | "words": false 137 | } 138 | ], 139 | "strict": 0, 140 | "wrap-regex": 0, 141 | "yoda": 0 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/cartridge/scripts/payment/common.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 'use strict'; 3 | 4 | var ArrayList = require('dw/util/ArrayList'); 5 | var List = require('dw/util/List'); 6 | var PaymentInstrument = require('dw/order/PaymentInstrument'); 7 | var PaymentMgr = require('dw/order/PaymentMgr'); 8 | 9 | /** 10 | * Validates payment instruments and returns valid payment instruments. 11 | * 12 | * @alias module:models/ProfileModel~ProfileModel/validateWalletPaymentInstruments 13 | * @param {dw.customer.Wallet|dw.order.Basket} paymentContainer - Entity that possesses payment instruments 14 | * @param {String} countryCode Billing country code or null. 15 | * @param {Number} amount Payment amount to check valid payment instruments for. 16 | * @returns {ArrayList} Returns an array with the valid PaymentInstruments. 17 | */ 18 | function validatePaymentInstruments(paymentContainer, countryCode, amount) { 19 | // Stripe changes BEGIN 20 | var stripeCheckoutHelper = require('*/cartridge/scripts/stripe/helpers/checkoutHelper'); 21 | var cardPaymentsHandledByStripe = stripeCheckoutHelper.isStripeCardsPaymentMethodEnabled(); 22 | // Stripe changes END 23 | 24 | var paymentInstruments = paymentContainer.getPaymentInstruments(); 25 | 26 | // Gets applicable payment methods. 27 | var methods = PaymentMgr.getApplicablePaymentMethods(customer, countryCode, amount); 28 | 29 | // Gets applicable payment cards from CREDIT_CARD payment method. 30 | var creditCardMethod = PaymentMgr.getPaymentMethod(PaymentInstrument.METHOD_CREDIT_CARD); 31 | var cards = creditCardMethod ? creditCardMethod.getApplicablePaymentCards(customer, countryCode, amount) : List.EMPTY_LIST; 32 | 33 | // Collects all invalid payment instruments. 34 | var validPaymentInstruments = new ArrayList(paymentInstruments); 35 | var invalidPaymentInstruments = new ArrayList(); 36 | 37 | for (var i = 0; i < paymentInstruments.length; i++) { 38 | var paymentInstrument = paymentInstruments[i]; 39 | 40 | // Ignores gift certificate payment instruments. 41 | if (PaymentInstrument.METHOD_GIFT_CERTIFICATE.equals(paymentInstrument.paymentMethod)) { 42 | continue; 43 | } 44 | 45 | // Gets a payment method. 46 | var method = PaymentMgr.getPaymentMethod(paymentInstrument.getPaymentMethod()); 47 | 48 | // Checks whether payment method is still applicable. 49 | if (method && methods.contains(method)) { 50 | // In case of method CREDIT_CARD, check payment cards 51 | // Stripe changes BEGIN 52 | // Do not check if card type is enabled in BM if card payments are handled by Stripe. 53 | if (!cardPaymentsHandledByStripe && PaymentInstrument.METHOD_CREDIT_CARD.equals(paymentInstrument.paymentMethod)) { 54 | // Stripe changes END 55 | 56 | // Gets payment card. 57 | var card = PaymentMgr.getPaymentCard(paymentInstrument.creditCardType); 58 | 59 | // Checks whether payment card is still applicable. 60 | if (card && cards.contains(card)) { 61 | continue; 62 | } 63 | } else { 64 | // Continues if method is applicable. 65 | continue; 66 | } 67 | } 68 | 69 | // Collects invalid payment instruments. 70 | invalidPaymentInstruments.add(paymentInstrument); 71 | validPaymentInstruments.remove(paymentInstrument); 72 | } 73 | 74 | if (invalidPaymentInstruments.size()) { 75 | return { 76 | InvalidPaymentInstruments: invalidPaymentInstruments, 77 | ValidPaymentInstruments: validPaymentInstruments 78 | }; 79 | } else { 80 | return { 81 | ValidPaymentInstruments: validPaymentInstruments 82 | }; 83 | } 84 | } 85 | 86 | module.exports = { 87 | validatePaymentInstruments: validatePaymentInstruments 88 | }; 89 | -------------------------------------------------------------------------------- /cartridges/app_stripe_sfra/cartridge/client/default/js/stripe.newcardform.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-alert */ 2 | /* eslint-disable no-plusplus */ 3 | /* eslint-disable require-jsdoc */ 4 | /* globals Stripe, $ */ 5 | 6 | 'use strict'; 7 | 8 | // v1 9 | var $form = $('.payment-form'); 10 | var stripe = Stripe(document.getElementById('stripePublicKey').value); 11 | var elements = stripe.elements(); 12 | 13 | function setCustomCardOutcome(result) { 14 | var displayError = document.getElementById('card-errors'); 15 | if (result.error) { 16 | displayError.textContent = result.error.message; 17 | } else { 18 | displayError.textContent = ''; 19 | } 20 | } 21 | 22 | var cardBrandToPfClass = { 23 | visa: 'pf-visa', 24 | mastercard: 'pf-mastercard', 25 | amex: 'pf-american-express', 26 | discover: 'pf-discover', 27 | diners: 'pf-diners', 28 | jcb: 'pf-jcb', 29 | unknown: 'pf-credit-card' 30 | }; 31 | 32 | function setCustomCardBrandIcon(brand) { 33 | var brandIconElement = document.getElementById('brand-icon'); 34 | var pfClass = 'pf-credit-card'; 35 | if (brand in cardBrandToPfClass) { 36 | pfClass = cardBrandToPfClass[brand]; 37 | } 38 | 39 | for (var i = brandIconElement.classList.length - 1; i >= 0; i--) { 40 | brandIconElement.classList.remove(brandIconElement.classList[i]); 41 | } 42 | brandIconElement.classList.add('pf'); 43 | brandIconElement.classList.add(pfClass); 44 | } 45 | 46 | var cardElement = null; 47 | var cardNumberElement = null; 48 | 49 | if (document.getElementById('card-element')) { 50 | cardElement = elements.create('card', { style: $form.data('element-style') }); 51 | cardElement.mount('#card-element'); 52 | } else if (document.getElementById('stripe-custom-card-group')) { 53 | var style = JSON.parse(document.getElementById('stripe-custom-card-group').dataset.elementstyle); 54 | 55 | cardNumberElement = elements.create('cardNumber', { 56 | style: style 57 | }); 58 | cardNumberElement.mount('#card-number-element'); 59 | 60 | var cardExpiryElement = elements.create('cardExpiry', { 61 | style: style 62 | }); 63 | cardExpiryElement.mount('#card-expiry-element'); 64 | 65 | var cardCvcElement = elements.create('cardCvc', { 66 | style: style 67 | }); 68 | cardCvcElement.mount('#card-cvc-element'); 69 | 70 | cardNumberElement.on('change', function (event) { 71 | // Switch brand logo 72 | if (event.brand) { 73 | setCustomCardBrandIcon(event.brand); 74 | } 75 | 76 | setCustomCardOutcome(event); 77 | }); 78 | } 79 | 80 | $('button[type="submit"]').on('click', function (e) { 81 | e.preventDefault(); 82 | var stripeCardEl = (!cardElement) ? cardNumberElement : cardElement; 83 | stripe.createPaymentMethod('card', stripeCardEl, { 84 | billing_details: { 85 | name: $('#cardOwner').val() 86 | } 87 | }).then(function (result) { 88 | if (result.error) { 89 | alert(result.error.message); 90 | } else { 91 | var paymentMethodId = result.paymentMethod.id; 92 | $.ajax({ 93 | url: $form.attr('action'), 94 | method: 'POST', 95 | data: { 96 | payment_method_id: paymentMethodId, 97 | csrf_token: $('[name="csrf_token"]').val() 98 | } 99 | }).done(function (msg) { 100 | if (msg.success) { 101 | window.location.href = $form.data('wallet-url'); 102 | } else { 103 | alert(msg.error); 104 | } 105 | }).fail(function (msg) { 106 | if (msg.responseJSON.redirectUrl) { 107 | window.location.href = msg.responseJSON.redirectUrl; 108 | } else { 109 | alert(msg); 110 | } 111 | }); 112 | } 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /cartridges/int_stripe_sfra/cartridge/scripts/hooks/payment/processor/stripe_credit.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* eslint-disable no-unused-vars */ 3 | /* eslint-disable new-cap */ 4 | /* global dw */ 5 | // v1 6 | 7 | 'use strict'; 8 | 9 | var collections = require('*/cartridge/scripts/util/collections'); 10 | 11 | var PaymentInstrument = require('dw/order/PaymentInstrument'); 12 | var PaymentMgr = require('dw/order/PaymentMgr'); 13 | var PaymentStatusCodes = require('dw/order/PaymentStatusCodes'); 14 | var Resource = require('dw/web/Resource'); 15 | var Transaction = require('dw/system/Transaction'); 16 | 17 | var StripeCreditHelper = require('*/cartridge/scripts/stripe/helpers/paymentprocessors/stripeCreditHelper'); 18 | 19 | /** 20 | * Verifies that entered credit card information is a valid card. If the information is valid a 21 | * credit card payment instrument is created 22 | * @param {dw.order.Basket} basket Current users's basket 23 | * @param {Object} paymentInformation - the payment information 24 | * @return {Object} returns an error object 25 | */ 26 | function Handle(basket, paymentInformation) { 27 | var serverErrors = []; 28 | 29 | var result = StripeCreditHelper.Handle({ Basket: basket }); 30 | if (result.errorMessage) { 31 | serverErrors.push(result.errorMessage); 32 | } 33 | return { fieldErrors: [], serverErrors: serverErrors, error: result.error }; 34 | } 35 | 36 | /** 37 | * Authorizes a payment using a credit card. Customizations may use other processors and custom 38 | * logic to authorize credit card payment. 39 | * @param {number} orderNumber - The current order's number 40 | * @param {dw.order.PaymentInstrument} paymentInstrument - The payment instrument to authorize 41 | * @param {dw.order.PaymentProcessor} paymentProcessor - The payment processor of the current 42 | * payment method 43 | * @return {Object} returns an error object 44 | */ 45 | function Authorize(orderNumber, paymentInstrument, paymentProcessor) { 46 | var serverErrors = []; 47 | var order = dw.order.OrderMgr.getOrder(orderNumber); 48 | 49 | var result = StripeCreditHelper.Authorize({ 50 | Order: order, OrderNo: orderNumber, PaymentInstrument: paymentInstrument, PaymentProcessor: paymentProcessor 51 | }); 52 | 53 | if (result.errorMessage) { 54 | serverErrors.push(result.errorMessage); 55 | } 56 | return { fieldErrors: [], serverErrors: serverErrors, error: result.error }; 57 | } 58 | 59 | exports.Handle = Handle; 60 | exports.Authorize = Authorize; 61 | 62 | // 'use strict'; 63 | 64 | /** 65 | * Verifies the required information for billing form is provided. 66 | * @param {Object} req - The request object 67 | * @param {Object} paymentForm - the payment form 68 | * @param {Object} viewFormData - object contains billing form data 69 | * @returns {Object} an object that has error information or payment information 70 | */ 71 | function processForm(req, paymentForm, viewFormData) { 72 | var viewData = viewFormData; 73 | const cardType = require('*/cartridge/scripts/stripe/helpers/cardsHelper').getCardType(); 74 | 75 | viewData.paymentMethod = { 76 | value: paymentForm.paymentMethod.value, 77 | htmlName: paymentForm.paymentMethod.value 78 | }; 79 | 80 | viewData.paymentInformation = { 81 | cardType: { 82 | value: cardType, 83 | htmlName: paymentForm.creditCardFields.cardType.htmlName 84 | } 85 | }; 86 | 87 | if (req.form.storedPaymentUUID) { 88 | viewData.storedPaymentUUID = req.form.storedPaymentUUID; 89 | } 90 | 91 | return { 92 | error: false, 93 | viewData: viewData 94 | }; 95 | } 96 | 97 | /** 98 | * Save the credit card information to login account if save card option is selected 99 | * @param {Object} req - The request object 100 | * @param {dw.order.Basket} basket - The current basket 101 | * @param {Object} billingData - payment information 102 | */ 103 | function savePaymentInformation(req, basket, billingData) { 104 | 105 | } 106 | 107 | exports.processForm = processForm; 108 | exports.savePaymentInformation = savePaymentInformation; 109 | -------------------------------------------------------------------------------- /cartridges/bm_stripe/cartridge/bm_extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Stripe 11 | Stripe Integration 12 | Stripe Integration 13 | icons/channel_ico.gif 14 | 15 | 16 | 17 | Stripe Quick Setup 18 | 19 | Stripe Quick Setup 20 | 21 | 22 | Stripe Quick Setup 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | icons/channel_ico.gif 34 | 35 | 36 | 37 | Stripe 38 | Stripe 39 | Stripe 40 | icons/channel_ico.gif 41 | 42 | 43 | 44 | Stripe Payment Methods Setup 45 | 46 | Stripe Payment Methods Setup 47 | 48 | 49 | Stripe Payment Methods Setup 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | icons/sitespaymentmethods_ico.gif 61 | 62 | 63 | 64 | Stripe Payments Refund 65 | 66 | Stripe Payments Refund 67 | 68 | 69 | Stripe Payments Refund 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | icons/sitespaymentmethods_ico.gif 81 | 82 | 83 | 84 | Stripe Payments Capture 85 | 86 | Stripe Payments Capture 87 | 88 | 89 | Stripe Payments Capture 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | icons/sitespaymentmethods_ico.gif 101 | 102 | 103 | -------------------------------------------------------------------------------- /tests/integration/data/addresses.js: -------------------------------------------------------------------------------- 1 | export default { 2 | shippingAddress: { 3 | firstName: 'John', 4 | lastName: 'Snow', 5 | address1: 'King\'s Landing Street', 6 | address2: ' ', 7 | country: 'US', 8 | state: 'AL', 9 | city: 'Winterfell', 10 | zip: '10002', 11 | phone: '3333333333' 12 | }, 13 | billing: { 14 | address: { 15 | firstName: 'John', 16 | lastName: 'Snow', 17 | address1: 'King\'s Landing Street', 18 | address2: '', 19 | country: 'US', 20 | states_stateCode: 'AL', 21 | city: 'Winterfell', 22 | postalCode: '10002' 23 | }, 24 | localizedNewAddressTitle: 'New Address', 25 | contact: { 26 | email: 'john.snow@winter.com', 27 | phone: '3333333333' 28 | }, 29 | paymentMethod: 'CREDIT_CARD', 30 | creditCard: { 31 | cardType: 'Visa', 32 | cardOwner: 'John Snow', 33 | cardNumber: '4242 4242 4242 4242', 34 | expirationDate: '03 / 23', 35 | securityCode: '222', 36 | postal: '12345' 37 | }, 38 | pmntsid: '0400Ia/vP4SUWbEXk1Rjuv1iJpZYtFlrhkK3C/jWjsnA+PFL7hXM+BvJMIwIbxGtg113arvZ30knyUaVrdhl24iyXJsZhQDshHSvaPPpXMdPssu8OKkxiidkfT/BZh4SFVrM2LXRd5PWWYj5BAOjEMysDN3f6hB1aXauYET2T7UUiYQhHvo+hGjjLi+eKIt0RPoUUueI7Dw/I726+7BeKgatck3mscLU3tbdm6EHdDac9RLfiX+O7TiFq9fjFJ1GqICXsR9WT0SMDOrPaaqU3XEsFyYg2wn1WUxgZNfbcjbO4PMRieADP31pI6gF1WPMlk0gO0SK5bmiUzWYY4NdZuSstIs//BPd61qfDe17C32orBF8Ae0mh/OSZsgH1DGeSyhahZfNQgu+lmSl2pKAmu1wluCISazs9PD83/seKcOlvXrGxhKauwqEWLxUN0sYF8r08f8uCcC0xODcb+CqMKE6vWvoUg55IcfZ2FeEyC96PI1MyPpjzYuuV+VhT2JQxvTdYYDWpT3oEwgMKXKx2z97+WEG0OQIme9YnwmMwTHLQ8wFqQHrhbycmr6iefIxytoco2Ji61Ck5rdiHsRUrA6QS4uoKmLN7sWceI0yl6E+DwcwZ8YN8wmMvb58v5uS+Xeh+PgUD7UE5t+3ycpeersqubmZnC9N+MV7Pw1IrivGG+RbFOaOQb/cq2N7RYtK+UivDX8icOWPZcy4Xo6JR+o8uNh2BeaMmRyXcWy+eyaOyUXme82CuN2k28xBySQ/FwSfOENgY4kVU9wB84+POrI4MkoD4iHJ5a1QF8AZkZDFo1mxfNqqh0E6sVkALA8VPQT2/XQXiHK5P5kWIqvLjsJp0rI+vS3zO1UWKyNRXh0mw7Kb1x4cMpEKIrjEjVFMhgLyi4PvWoIpyZmpr5zlQx90s1eovP7W+WpQCv10TaNx+XTCCuhAF+nK4rUXuVmIvrx646dWeJWfjoVVPDYLihDanq9ZzbT/DhApJP+uJSqKjN8A6K3HjRQPNotrE1YwzZLxRONsrNmlQ0HEpvsUb5+Jr58UIS8xP1xzpdqSgJrtcJbn4KW2br+bRU8xAXTS4l7DQtPYEf0PHxnrIWh2D/Dqugiu1FR73ys3gJnjRx+vsOqqLsoVIzcv/8w7GK4E4KSx5gHMQuaEdG+HNurvDqdhY+sgvf4kz7YfS4OQSXqf8xt2a8rTDJbjdov8LoqeNX0WO8CqeLIiJFLlP/5QtzW5fUJtBtGitOTxWEzxY+eOSlhauODchk2WrH2LOmVQKLUdYeTcLtcejyCxPk2UkqDvdYEYDfmfBwBYRhc3f5urJ1aotlycMEBL1IzyVekwhI5/tPvXfQEg+KLRVsDNUznM+4IlRQO86SD0OIS7LH591kg7Q92FGKueu33EPopga4/ZJlT9tKJit83WRnALzAimFMFv6Nnri8MH5cjoRMpCM8L0AI9RRuWj3H/7/U2KmI6OzrvBJS02FCAJ3mbZGg4G6Pe43Q6pFh1NoVwxtZ8dY/a9DS5zbA5raYD4PraEB1tXTn4iHVZcLANSCzX4rb3Z3v6pz9bBhAgo' 39 | }, 40 | cards: { 41 | visa: { 42 | number: '4242 4242 4242 4242', 43 | date: '12 / 24', 44 | cvc: '123', 45 | zip: '12345' 46 | }, 47 | visaDebit: { 48 | number: '4000 0566 5566 5556', 49 | date: '12 / 24', 50 | cvc: '123', 51 | zip: '12345' 52 | }, 53 | mastercard: { 54 | number: '5555 5555 5555 4444', 55 | date: '12 / 24', 56 | cvc: '123', 57 | zip: '12345' 58 | }, 59 | mastercard2: { 60 | number: '2223 0031 2200 3222', 61 | date: '12 / 24', 62 | cvc: '123', 63 | zip: '12345' 64 | }, 65 | mastercardDebit: { 66 | number: '5200 8282 8282 8210', 67 | date: '12 / 24', 68 | cvc: '123', 69 | zip: '12345' 70 | }, 71 | mastercardPrepaid: { 72 | number: '5105 1051 0510 5100', 73 | date: '12 / 24', 74 | cvc: '123', 75 | zip: '12345' 76 | }, 77 | americanExpress: { 78 | number: '3782 822463 10005', 79 | date: '12 / 24', 80 | cvc: '1234', 81 | zip: '12345' 82 | }, 83 | americanExpress2: { 84 | number: '3714 496353 98431', 85 | date: '12 / 24', 86 | cvc: '1234', 87 | zip: '12345' 88 | }, 89 | visa3Dsequre: { 90 | number: '4000 0000 0000 3220', 91 | date: '12 / 24', 92 | cvc: '123', 93 | zip: '12345' 94 | } 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/static/default/js/stripe.newcardform.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-alert */ 2 | /* globals cardElement, cardNumberElement, Stripe */ 3 | // v1 4 | var $form = $('.payment-form'); 5 | var stripe = Stripe(document.getElementById('stripePublicKey').value); 6 | var elements = stripe.elements(); 7 | 8 | function setCustomCardOutcome(result) { 9 | var displayError = document.getElementById('card-errors'); 10 | if (result.error) { 11 | displayError.textContent = result.error.message; 12 | } else { 13 | displayError.textContent = ''; 14 | } 15 | } 16 | 17 | var cardBrandToPfClass = { 18 | visa: 'pf-visa', 19 | mastercard: 'pf-mastercard', 20 | amex: 'pf-american-express', 21 | discover: 'pf-discover', 22 | diners: 'pf-diners', 23 | jcb: 'pf-jcb', 24 | unknown: 'pf-credit-card' 25 | }; 26 | 27 | function setCustomCardBrandIcon(brand) { 28 | var brandIconElement = document.getElementById('brand-icon'); 29 | var pfClass = 'pf-credit-card'; 30 | if (brand in cardBrandToPfClass) { 31 | pfClass = cardBrandToPfClass[brand]; 32 | } 33 | 34 | for (var i = brandIconElement.classList.length - 1; i >= 0; i--) { 35 | brandIconElement.classList.remove(brandIconElement.classList[i]); 36 | } 37 | brandIconElement.classList.add('pf'); 38 | brandIconElement.classList.add(pfClass); 39 | } 40 | 41 | window.cardElement = null; 42 | window.cardNumberElement = null; 43 | 44 | if (document.getElementById('card-element')) { 45 | window.cardElement = elements.create('card', { style: $form.data('element-style') }); 46 | window.cardElement.mount('#card-element'); 47 | } else if (document.getElementById('stripe-custom-card-group')) { 48 | var style = JSON.parse(document.getElementById('stripe-custom-card-group').dataset.elementstyle); 49 | 50 | window.cardNumberElement = elements.create('cardNumber', { 51 | style: style 52 | }); 53 | window.cardNumberElement.mount('#card-number-element'); 54 | 55 | var cardExpiryElement = elements.create('cardExpiry', { 56 | style: style 57 | }); 58 | cardExpiryElement.mount('#card-expiry-element'); 59 | 60 | var cardCvcElement = elements.create('cardCvc', { 61 | style: style 62 | }); 63 | cardCvcElement.mount('#card-cvc-element'); 64 | 65 | window.cardNumberElement.on('change', function (event) { 66 | // Switch brand logo 67 | if (event.brand) { 68 | setCustomCardBrandIcon(event.brand); 69 | } 70 | 71 | setCustomCardOutcome(event); 72 | }); 73 | } 74 | 75 | var $cardHolderNameInput = $('#stripe-cardholder-name'); 76 | 77 | function closeDialog() { 78 | var $dialogContainer = $('#dialog-container'); 79 | if ($dialogContainer.length) { 80 | $dialogContainer.dialog('close'); 81 | } 82 | } 83 | 84 | var cancelBtn = document.getElementById('stripeCancelBtn'); 85 | cancelBtn.addEventListener('click', function () { 86 | closeDialog(); 87 | }); 88 | 89 | var $addCardBtn = $('#stripeApplyBtn'); 90 | $addCardBtn.on('click', function () { 91 | // https://stripe.com/docs/payments/payment-methods/saving 92 | 93 | var stripeCardEl = (!window.cardElement) ? window.cardNumberElement : window.cardElement; 94 | stripe.createPaymentMethod('card', stripeCardEl, { 95 | billing_details: { 96 | name: $cardHolderNameInput.val() 97 | } 98 | }).then(function (result) { 99 | if (result.error) { 100 | alert(result.error.message); 101 | } else { 102 | var paymentMethodId = result.paymentMethod.id; 103 | $.ajax({ 104 | url: $form.attr('action'), 105 | method: 'POST', 106 | data: { 107 | payment_method_id: paymentMethodId, 108 | csrf_token: $('[name="csrf_token"]').val() 109 | } 110 | }).done(function (msg) { 111 | if (msg.success) { 112 | // eslint-disable-next-line no-restricted-globals 113 | location.reload(); 114 | } else { 115 | alert(msg.error); 116 | } 117 | }).fail(function (msg) { 118 | if (msg.responseJSON.redirectUrl) { 119 | window.location.href = msg.responseJSON.redirectUrl; 120 | } else { 121 | alert(msg); 122 | } 123 | }); 124 | } 125 | }); 126 | closeDialog(); 127 | }); 128 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/steptypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "step-types": { 3 | "script-module-step": [ 4 | { 5 | "@type-id": "custom.StripeDeleteCustomObjects", 6 | "@supports-parallel-execution": "false", 7 | "@supports-site-context": "true", 8 | "@supports-organization-context": "false", 9 | "description": "Removes custom objects of selected statuses", 10 | "module": "int_stripe_core/cartridge/scripts/stripe/jobs/deleteCustomObjectsStep.js", 11 | "transactional": "false", 12 | "timeout-in-seconds": "18000", 13 | "parameters": { 14 | "parameter": [ 15 | { 16 | "@name": "UNKNOWN", 17 | "@type": "boolean", 18 | "@required": "false", 19 | "@trim": "true", 20 | "description": "Remove custom objects having status UNKNOWN" 21 | }, 22 | { 23 | "@name": "PROCESSED", 24 | "@type": "boolean", 25 | "@required": "false", 26 | "@trim": "true", 27 | "description": "Remove custom objects having status PROCESSED" 28 | }, 29 | { 30 | "@name": "PROCESS", 31 | "@type": "boolean", 32 | "@required": "false", 33 | "@trim": "true", 34 | "description": "Remove custom objects having status PROCESS" 35 | }, 36 | { 37 | "@name": "FAIL_OR_CANCEL", 38 | "@type": "boolean", 39 | "@required": "false", 40 | "@trim": "true", 41 | "description": "Remove custom objects having status FAIL_OR_CANCEL" 42 | }, 43 | { 44 | "@name": "PENDING_CHARGE", 45 | "@type": "boolean", 46 | "@required": "false", 47 | "@trim": "true", 48 | "description": "Remove custom objects having status PENDING_CHARGE" 49 | } 50 | ] 51 | }, 52 | "status-codes": { 53 | "status": [ 54 | { 55 | "@code": "ERROR", 56 | "description": "Used when the step failed with an error." 57 | }, 58 | { 59 | "@code": "OK", 60 | "description": "Used when the step finished successfully." 61 | }, 62 | { 63 | "@code": "FINISHED_WITH_WARNINGS", 64 | "description": "Used when the step finished with warnings." 65 | } 66 | ] 67 | } 68 | }, 69 | { 70 | "@type-id": "custom.StripeProcessWebhookNotifications", 71 | "@supports-parallel-execution": "false", 72 | "@supports-site-context": "true", 73 | "@supports-organization-context": "false", 74 | "description": "Processes webhooks notifications received from Stripe and saved in custom objects", 75 | "module": "int_stripe_core/cartridge/scripts/stripe/jobs/processSavedNotifications.js", 76 | "transactional": "false", 77 | "timeout-in-seconds": "18000", 78 | "status-codes": { 79 | "status": [ 80 | { 81 | "@code": "ERROR", 82 | "description": "Used when the step failed with an error." 83 | }, 84 | { 85 | "@code": "OK", 86 | "description": "Used when the step finished successfully." 87 | }, 88 | { 89 | "@code": "FINISHED_WITH_WARNINGS", 90 | "description": "Used when the step finished with warnings." 91 | } 92 | ] 93 | } 94 | } 95 | ] 96 | } 97 | } -------------------------------------------------------------------------------- /cartridges/app_stripe_core/cartridge/scripts/checkout/ValidatePaymentInstruments.ds: -------------------------------------------------------------------------------- 1 | /** 2 | * Verifies whether existing non-gift-certificate payment instrument method / cards 3 | * are still applicable on base of the given attributes. 4 | * Puts the collection of valid and invalid payment instruments into pipeline dictionary. 5 | * 6 | * @input PaymentInstruments : dw.util.Collection The collection of payment instruments to validate. 7 | * @input Customer : dw.customer.Customer The current customer. 8 | * @input CountryCode : String The country code. 9 | * @input PaymentAmount : Number The payment amount. 10 | * @output ValidPaymentInstruments : dw.util.Collection The collection of valid payment instruments. 11 | * @output InvalidPaymentInstruments : dw.util.Collection The collection of invalid payment instruments. 12 | */ 13 | importPackage( dw.customer ); 14 | importPackage( dw.object ); 15 | importPackage( dw.order ); 16 | importPackage( dw.system ); 17 | importPackage( dw.util); 18 | importPackage( dw.value ); 19 | 20 | importScript("checkout/Utils.ds"); 21 | 22 | function execute( pdict : PipelineDictionary ) : Number 23 | { 24 | // Stripe changes BEGIN 25 | var stripeCheckoutHelper = require('*/cartridge/scripts/stripe/helpers/checkoutHelper'); 26 | var cardPaymentsHandledByStripe = stripeCheckoutHelper.isStripeCardsPaymentMethodEnabled(); 27 | // Stripe changes END 28 | 29 | // get basket from pipeline dictionary 30 | var paymentInstruments : Collection = pdict.PaymentInstruments; 31 | 32 | // get customer from pipeline dictionary 33 | var customer : Customer = pdict.Customer; 34 | 35 | // get country code from pipeline dictionary 36 | var countryCode = pdict.CountryCode; 37 | 38 | // get payment amount from pipeline dictionary 39 | var amount : Number = pdict.PaymentAmount; 40 | 41 | // get applicable payment methods 42 | var methods : List = PaymentMgr.getApplicablePaymentMethods(customer, countryCode, amount); 43 | 44 | // get applicable payment cards from CREDIT_CARD payment method 45 | var ccMethod : PaymentMethod = PaymentMgr.getPaymentMethod(PaymentInstrument.METHOD_CREDIT_CARD); 46 | var cards : List = ccMethod != null ? ccMethod.getApplicablePaymentCards(customer, countryCode, amount) : List.EMPTY_LIST; 47 | 48 | // collects all not applicable payment instruments 49 | var validPaymentInstruments : Collection = new ArrayList(paymentInstruments); 50 | var invalidPaymentInstruments : Collection = new ArrayList(); 51 | 52 | // get payment instruments from basket 53 | for each( var pi : PaymentInstrument in paymentInstruments ) 54 | { 55 | // ignore gift certificate payment instruments 56 | if( PaymentInstrument.METHOD_GIFT_CERTIFICATE.equals(pi.paymentMethod) ) 57 | { 58 | continue; 59 | } 60 | 61 | // get payment method 62 | var method : PaymentMethod = PaymentMgr.getPaymentMethod(pi.getPaymentMethod()); 63 | 64 | // check, whether payment method is still applicable 65 | if( method != null && contains(method, methods) ) 66 | { 67 | // in case of method CREDIT_CARD, check payment cards 68 | // Stripe changes BEGIN 69 | // Do not check if card type is enabled in BM if card payments are handled by Stripe. 70 | if(!cardPaymentsHandledByStripe && PaymentInstrument.METHOD_CREDIT_CARD.equals(pi.paymentMethod) ) 71 | // Stripe changes END 72 | { 73 | // get payment card 74 | var card : PaymentCard = PaymentMgr.getPaymentCard(pi.creditCardType); 75 | 76 | // check, whether payment card is still applicable 77 | if( card != null && contains(card, cards) ) 78 | { 79 | continue; 80 | } 81 | } 82 | else 83 | { 84 | // continue, if method is applicable 85 | continue; 86 | } 87 | } 88 | 89 | // collect invalid payment instruments 90 | invalidPaymentInstruments.add(pi); 91 | validPaymentInstruments.remove(pi); 92 | } 93 | 94 | pdict["ValidPaymentInstruments"] = validPaymentInstruments; 95 | 96 | if( !invalidPaymentInstruments.empty ) 97 | { 98 | pdict["InvalidPaymentInstruments"] = invalidPaymentInstruments; 99 | return PIPELET_ERROR; 100 | } 101 | 102 | return PIPELET_NEXT; 103 | } 104 | 105 | function contains(object : PersistentObject, objects : Collection) 106 | { 107 | for each( var anObject : PersistentObject in objects ) 108 | { 109 | if( object.UUID.equals( anObject.UUID ) ) 110 | { 111 | return true; 112 | } 113 | } 114 | 115 | return false; 116 | } 117 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/checkout/billing/cardform.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
style="display:none;"> 15 |
16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 | 37 |
38 | 39 |
 
40 |
41 | 42 | 45 | 46 |
47 | 48 |
49 | 50 |
51 | 52 |
53 | checked 64 | > 65 | 68 |
69 |
70 | 77 |
78 |
79 |
80 | 81 | 82 |
83 | 84 | 85 |
86 | 87 | 88 |
89 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/templates/default/stripe/checkout/billing/sfra_cardform.isml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 |
30 | 31 |
32 | 33 | 34 |
35 | 36 |
 
37 |
38 | 39 | 42 | 43 |
44 | 45 |
46 | 47 | 48 |
49 | checked 60 | > 61 | 66 |
67 |
68 | 75 |
76 |
77 | 78 | 79 |
80 | 81 | 82 |
83 | 84 | 85 |
86 | -------------------------------------------------------------------------------- /cartridges/app_stripe_controllers/cartridge/controllers/COSummary.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 'use strict'; 3 | 4 | /** 5 | * This controller implements the last step of the checkout. A successful handling 6 | * of billing address and payment method selection leads to this controller. It 7 | * provides the customer with a last overview of the basket prior to confirm the 8 | * final order creation. 9 | * 10 | * @module controllers/COSummary 11 | */ 12 | 13 | /* API Includes */ 14 | var Resource = require('dw/web/Resource'); 15 | var Transaction = require('dw/system/Transaction'); 16 | var URLUtils = require('dw/web/URLUtils'); 17 | 18 | /* Script Modules */ 19 | var app = require('*/cartridge/scripts/app'); 20 | var guard = require('*/cartridge/scripts/guard'); 21 | 22 | var Cart = app.getModel('Cart'); 23 | 24 | /** 25 | * Renders the summary page prior to order creation. 26 | * @param {Object} context context object used for the view 27 | */ 28 | function start(context) { 29 | var cart = Cart.get(); 30 | 31 | // Checks whether all payment methods are still applicable. Recalculates all existing non-gift certificate payment 32 | // instrument totals according to redeemed gift certificates or additional discounts granted through coupon 33 | // redemptions on this page. 34 | var COBilling = require('./COBilling'); 35 | if (!COBilling.ValidatePayment(cart)) { 36 | COBilling.Start(); 37 | return; 38 | } else { 39 | Transaction.wrap(function () { 40 | cart.calculate(); 41 | }); 42 | 43 | Transaction.wrap(function () { 44 | if (!cart.calculatePaymentTransactionTotal()) { 45 | COBilling.Start(); 46 | } 47 | }); 48 | 49 | var pageMeta = require('*/cartridge/scripts/meta'); 50 | var viewContext = require('*/cartridge/scripts/common/extend').immutable(context, { 51 | Basket: cart.object 52 | }); 53 | pageMeta.update({pageTitle: Resource.msg('summary.meta.pagetitle', 'checkout', 'SiteGenesis Checkout')}); 54 | app.getView(viewContext).render('checkout/summary/summary'); 55 | } 56 | } 57 | 58 | /** 59 | * This function is called when the "Place Order" action is triggered by the 60 | * customer. 61 | */ 62 | function submit() { 63 | // Calls the COPlaceOrder controller that does the place order action and any payment authorization. 64 | // COPlaceOrder returns a JSON object with an order_created key and a boolean value if the order was created successfully. 65 | // If the order creation failed, it returns a JSON object with an error key and a boolean value. 66 | var placeOrderResult = require('./COPlaceOrder').Start(); 67 | if (placeOrderResult.error) { 68 | start({ 69 | PlaceOrderError: placeOrderResult.PlaceOrderError 70 | }); 71 | } else if (placeOrderResult.order_created) { 72 | showConfirmation(placeOrderResult.Order); 73 | } 74 | } 75 | 76 | /** 77 | * Renders the order confirmation page after successful order 78 | * creation. If a nonregistered customer has checked out, the confirmation page 79 | * provides a "Create Account" form. This function handles the 80 | * account creation. 81 | */ 82 | function showConfirmation(order) { 83 | if (!customer.authenticated) { 84 | // Initializes the account creation form for guest checkouts by populating the first and last name with the 85 | // used billing address. 86 | var customerForm = app.getForm('profile.customer'); 87 | customerForm.setValue('firstname', order.billingAddress.firstName); 88 | customerForm.setValue('lastname', order.billingAddress.lastName); 89 | customerForm.setValue('email', order.customerEmail); 90 | customerForm.setValue('orderNo', order.orderNo); 91 | } 92 | 93 | app.getForm('profile.login.passwordconfirm').clear(); 94 | app.getForm('profile.login.password').clear(); 95 | 96 | var pageMeta = require('*/cartridge/scripts/meta'); 97 | pageMeta.update({pageTitle: Resource.msg('confirmation.meta.pagetitle', 'checkout', 'SiteGenesis Checkout Confirmation')}); 98 | app.getView({ 99 | Order: order, 100 | ContinueURL: URLUtils.https('Account-RegistrationForm') // needed by registration form after anonymous checkouts 101 | }).render('checkout/confirmation/confirmation'); 102 | } 103 | 104 | /* 105 | * Module exports 106 | */ 107 | 108 | /* 109 | * Web exposed methods 110 | */ 111 | /** @see module:controllers/COSummary~Start */ 112 | exports.Start = guard.ensure(['https'], start); 113 | /** @see module:controllers/COSummary~Submit */ 114 | exports.Submit = guard.ensure(['https', 'post', 'csrf'], submit); 115 | 116 | /* 117 | * Local method 118 | */ 119 | exports.ShowConfirmation = showConfirmation; 120 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 24.0.1 (2025-10-22) 4 | * Removed documentation from repository 5 | * Fixed an issue where the site preference for billing details collection on payment element was not working poperly 6 | * Removed code duplication on client side and refactored some functions 7 | 8 | ## 24.0.0 (2025-09-09) 9 | * Bank transfer implementation as a new payment method 10 | * Fixed an issue where changing the shipping address in the express checkout popup was not updating the shipping methods 11 | 12 | ## 23.9.1 (2025-08-11) 13 | * Removed duplicated API key from service headers 14 | * Populated stripeAccountID site preference within Quick Setup BM module 15 | 16 | ## 23.9.0 (2025-08-06) 17 | * Controllers and client side js refactoring and optimization 18 | * Customer sessions Implementation (replacing old saved cards tab that was using Card Payment Logic) 19 | * APM cvc recollection 20 | * Minicart ECE 21 | 22 | ## 23.8.0 (2025-02-18) 23 | * Added Multi Capture Option as a configuration 24 | * Card payment refactoring and JS updated to use handleNextAction instead of handleCardAction 25 | 26 | ## 23.7.0 (2024-12-09) 27 | * Removed PRB related functonality 28 | * Payment element now creates confirmation token before initalizing the payment. PaymentIntent creation now takes the Confirmation Token as a parameter and automatically confirms it. 29 | 30 | ## 23.6.0 (2024-09-05) 31 | * Reduced number of API calls by merging and using expand option to include customer data in the stripeService.paymentMethods.list method from stripeWallet.js -> fetchSavedPaymentInstruments() 32 | 33 | ## 23.5.0 (2024-07-01) 34 | * Express Checkout for SFRA and Site Genesis 35 | 36 | ## 23.4.0 (2024-01-18) 37 | * Business Manager module for capturing payments 38 | * Added the option to include Shipping Details in the payment intent payload 39 | * Fixed an issue where a model was crashing the website when the session was expiring during the checkout 40 | * Fixed an issue where closing 3DS popup right after confirming it on mobile was resulting in a successful payment but a failed order 41 | * Removed URL brackets from the service header. 42 | 43 | ## 23.2.0 (2023-04-24) 44 | * Stripe Payment Element: Defer intent/decoupled payment intent creation on init of Stripe Payment Element widget 45 | * Stripe Card Payments: Bug fix redirecting to Order Completed Page instead of Order failure page on 3DS card decline 46 | 47 | ## 23.2.0 (2023-03-03) 48 | * fix Saved Card functionality - not saving cards when capture mode is enabled 49 | * merge confirmPaymentIntent and placeOrder in one request instead of separate AJAX calls 50 | * merge create SFCC order and Stripe payment intent into one request instead of separate AJAX calls 51 | * clean up session privacy variable for Stripe Order Id after placing an order 52 | 53 | ## 23.1.0 (2023-02-06) 54 | * Card payments: add additional check to generate new payment intent on place order 55 | * Stripe Payment Element: Init card details for Card Payments in OMS 56 | 57 | 58 | ## 22.4.0 (2022-12-10) 59 | * clean up deprecated payment methods 60 | * Card payments: create SFCC order before making a call to Stripe to authorize/capture the payment 61 | * fix issues order confirmation email locales 62 | * fix issues Payment discrepancy issue and e-mail order confirm sent twice 63 | * fix issue with OM app orders sync 64 | * Note: In 22.4.0 we have made major changes (as a hotfix) of the Checkout flow when create a Payment Intents in Stripe and creating of SFCC orders. For card payments, the SFCC order is being created first, then on success we create a new payment intent and make a call to Stripe to authorize/capture. For Stripe Payment Elements, the SFCC order also is being created first in status ‘Created’, then on success in SFCC there should be receive Stripe payment_intent.succeeded webhook which will be processed then by ‘Stripe – Process Webhook Notifications’ job which will change the order status accordingly. 65 | 66 | ## 22.3.0 (2022-11-14) 67 | * fix issue with not writing meta data to payment intent 68 | 69 | ## 22.2.0 (2022-07-01) 70 | * add Stripe Radar Support 71 | 72 | ## 22.1.0 (2022-06-02) 73 | * add Stripe Payment Element 74 | 75 | ## 21.3.0 (2021-09-13) 76 | * add OM app support 77 | * add EPS 78 | * add P24 79 | 80 | ## 21.2.0 (2021-06-01) 81 | * add Paypal (beta) support 82 | 83 | ## 21.1.0 (2021-03-19) 84 | * add Multibanco 85 | * add credit card form customizable 86 | * add quick setup BM page 87 | * add Klarna support 88 | 89 | ## 20.2.0 (2020-07-22) 90 | * add ACH debit 91 | * add WeChat pay 92 | 93 | ## 20.1.0 (2020-02-01) 94 | * Update documentation to match the new Salesforce template 95 | 96 | ## 18.1.0 (2019-04-15) 97 | * Update to use Stripe elements, sources, payment request button, webhooks and asynchronous payments 98 | 99 | ## 16.1.0 (2019-07-30) 100 | * Initial release 101 | 102 | -------------------------------------------------------------------------------- /cartridges/int_stripe_controllers/cartridge/controllers/Stripe.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* global request, dw, response, customer, session */ 3 | 4 | 'use strict'; 5 | 6 | var URLUtils = require('dw/web/URLUtils'); 7 | var app = require('*/cartridge/scripts/app'); 8 | var OrderMgr = require('dw/order/OrderMgr'); 9 | var Resource = require('dw/web/Resource'); 10 | var stripeCheckoutHelper = require('*/cartridge/scripts/stripe/helpers/checkoutHelper'); 11 | 12 | /** 13 | * Entry point for processing webhooks push notifications. 14 | */ 15 | function webHook() { 16 | const webhooksHelper = require('*/cartridge/scripts/stripe/helpers/webhooksHelper'); 17 | webhooksHelper.processIncomingNotification(); 18 | } 19 | 20 | exports.WebHook = webHook; 21 | exports.WebHook.public = true; 22 | 23 | /** 24 | * Return shipping options used for payment request button 25 | */ 26 | function getShippingOptions() { 27 | response.setContentType('application/json'); 28 | response.writer.print(JSON.stringify(require('*/cartridge/scripts/stripe/helpers/checkoutHelper').getShippingOptionsSiteGenesis(request.httpParameterMap))); 29 | } 30 | 31 | exports.GetShippingOptions = getShippingOptions; 32 | exports.GetShippingOptions.public = true; 33 | 34 | /** 35 | * Display Order summary page for Stripe Payment Element Orders 36 | */ 37 | function paymentElementOrderPlaced() { 38 | if (!session.privacy.stripeOrderNumber) { 39 | response.redirect(URLUtils.https('Cart-Show')); 40 | return; 41 | } 42 | 43 | var order = OrderMgr.getOrder(session.privacy.stripeOrderNumber); 44 | if (!order) { 45 | response.redirect(URLUtils.https('Cart-Show')); 46 | return; 47 | } 48 | 49 | if (!customer.authenticated) { 50 | // Initializes the account creation form for guest checkouts by populating the first and last name with the 51 | // used billing address. 52 | var customerForm = app.getForm('profile.customer'); 53 | customerForm.setValue('firstname', order.billingAddress.firstName); 54 | customerForm.setValue('lastname', order.billingAddress.lastName); 55 | customerForm.setValue('email', order.customerEmail); 56 | customerForm.setValue('orderNo', order.orderNo); 57 | } 58 | 59 | app.getForm('profile.login.passwordconfirm').clear(); 60 | app.getForm('profile.login.password').clear(); 61 | 62 | var pageMeta = require('*/cartridge/scripts/meta'); 63 | pageMeta.update({ 64 | pageTitle: Resource.msg('confirmation.meta.pagetitle', 'checkout', 'SiteGenesis Checkout Confirmation') 65 | }); 66 | 67 | app.getView({ 68 | Order: order, 69 | ContinueURL: URLUtils.https('Account-RegistrationForm') // needed by registration form after anonymous checkouts 70 | }).render('checkout/confirmation/confirmation'); 71 | } 72 | 73 | exports.PaymentElementOrderPlaced = paymentElementOrderPlaced; 74 | exports.PaymentElementOrderPlaced.public = true; 75 | 76 | /** 77 | * Performs further steps after order is successfully placed with card form 78 | */ 79 | function cardOrderPlaced() { 80 | if (!session.privacy.stripeOrderNumber) { 81 | response.redirect(URLUtils.https('Cart-Show')); 82 | return; 83 | } 84 | 85 | var order = OrderMgr.getOrder(session.privacy.stripeOrderNumber); 86 | if (!order) { 87 | response.redirect(URLUtils.https('Cart-Show')); 88 | return; 89 | } 90 | 91 | var isAPMOrder = stripeCheckoutHelper.isAPMOrder(order); 92 | if (!isAPMOrder) { 93 | session.privacy.stripeOrderNumber = null; 94 | delete session.privacy.stripeOrderNumber; 95 | } 96 | 97 | if (!customer.authenticated) { 98 | // Initializes the account creation form for guest checkouts by populating the first and last name with the 99 | // used billing address. 100 | var customerForm = app.getForm('profile.customer'); 101 | customerForm.setValue('firstname', order.billingAddress.firstName); 102 | customerForm.setValue('lastname', order.billingAddress.lastName); 103 | customerForm.setValue('email', order.customerEmail); 104 | customerForm.setValue('orderNo', order.orderNo); 105 | } 106 | 107 | app.getForm('profile.login.passwordconfirm').clear(); 108 | app.getForm('profile.login.password').clear(); 109 | 110 | var pageMeta = require('*/cartridge/scripts/meta'); 111 | pageMeta.update({ 112 | pageTitle: Resource.msg('confirmation.meta.pagetitle', 'checkout', 'SiteGenesis Checkout Confirmation') 113 | }); 114 | 115 | app.getView({ 116 | Order: order, 117 | ContinueURL: URLUtils.https('Account-RegistrationForm') // needed by registration form after anonymous checkouts 118 | }).render('checkout/confirmation/confirmation'); 119 | } 120 | 121 | exports.CardOrderPlaced = cardOrderPlaced; 122 | exports.CardOrderPlaced.public = true; 123 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/helpers/controllers/stripePaymentsHelper.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* global request, response, dw, empty, customer, session */ 3 | 4 | 'use strict'; 5 | 6 | const OrderMgr = require('dw/order/OrderMgr'); 7 | const Transaction = require('dw/system/Transaction'); 8 | 9 | /** 10 | * Entry point for writing errors to Stripe Logger 11 | * @param {string} msg - error message to be logged 12 | */ 13 | function logStripeErrorMessage(msg) { 14 | if (empty(msg)) { 15 | return; 16 | } 17 | 18 | const Logger = require('dw/system/Logger').getLogger('Stripe', 'stripe'); 19 | 20 | Logger.error('Error: {0}', msg); 21 | } 22 | 23 | exports.LogStripeErrorMessage = logStripeErrorMessage; 24 | exports.LogStripeErrorMessage.public = true; 25 | 26 | /** 27 | * Entry point for fail of Stripe Order 28 | */ 29 | function failOrder() { 30 | try { 31 | if (session.privacy.stripeOrderNumber) { 32 | var order = OrderMgr.getOrder(session.privacy.stripeOrderNumber); 33 | if (order) { 34 | Transaction.wrap(function () { 35 | order.addNote('Order Failed Reason', 'failed from storefront Stripe.js: stripe.confirmPayment (for Stripe Payment Elements) or stripe.handleCardAction (for Card Payments)'); 36 | OrderMgr.failOrder(order, true); 37 | }); 38 | } 39 | session.privacy.stripeOrderNumber = null; 40 | delete session.privacy.stripeOrderNumber; 41 | } 42 | } catch (e) { 43 | logStripeErrorMessage('stripePaymentsHelper.js failOrder error: ' + e.message); 44 | } 45 | } 46 | 47 | exports.FailOrder = failOrder; 48 | exports.FailOrder.public = true; 49 | 50 | /** 51 | * An entry point to handle returns from alternative payment methods. 52 | * @param {Object} sfra - . 53 | * @return {Object} redirectUrl. 54 | */ 55 | function handleAPM(sfra) { 56 | const URLUtils = require('dw/web/URLUtils'); 57 | const paramsMap = request.httpParameterMap; 58 | 59 | const sourceId = paramsMap.source ? paramsMap.source.stringValue : null; 60 | const sourceClientSecret = paramsMap.client_secret ? paramsMap.client_secret.stringValue : null; 61 | 62 | const paymentIntentId = paramsMap.payment_intent ? paramsMap.payment_intent.stringValue : null; 63 | const paymentIntentClientSecret = paramsMap.payment_intent_client_secret ? paramsMap.payment_intent_client_secret.stringValue : null; 64 | 65 | var redirectUrl = ''; 66 | try { 67 | const stripeService = require('*/cartridge/scripts/stripe/services/stripeService'); 68 | 69 | // handle payments with source id 70 | if (!empty(sourceId)) { 71 | const source = stripeService.sources.retrieve(sourceId); 72 | 73 | if (source.client_secret !== sourceClientSecret) { 74 | throw new Error('Source client secret mismatch'); 75 | } 76 | 77 | if (['chargeable', 'pending'].indexOf(source.status) < 0) { 78 | throw new Error('Source not authorized.'); 79 | } 80 | } 81 | 82 | // handle payment intents 83 | if (!empty(paymentIntentId)) { 84 | const paymentIntent = stripeService.paymentIntents.retrieve(paymentIntentId); 85 | 86 | if (paymentIntent.client_secret !== paymentIntentClientSecret) { 87 | throw new Error('PaymentIntent client secret mismatch'); 88 | } 89 | 90 | if (['requires_source', 'requires_payment_method'].indexOf(paymentIntent.status) >= 0) { 91 | throw new Error('Payment intent failed.'); 92 | } 93 | 94 | // Set a cookie to authenticate customers for Link 95 | if ((paymentIntent.status === 'succeeded' || paymentIntent.status === 'processing' || paymentIntent.status === 'requires_capture') && paymentIntent.payment_method) { 96 | const paymentMethod = stripeService.paymentMethods.retrieve(paymentIntent.payment_method); 97 | if (paymentMethod && paymentMethod.link && paymentMethod.link.persistent_token) { 98 | var stripeCookie = new dw.web.Cookie('stripe.link.persistent_token', paymentMethod.link.persistent_token); 99 | stripeCookie.setSecure(true); 100 | stripeCookie.setHttpOnly(true); 101 | stripeCookie.setMaxAge(90 * 24 * 3600); 102 | 103 | response.addHttpCookie(stripeCookie); 104 | } 105 | } 106 | } 107 | 108 | if (sfra) { 109 | redirectUrl = URLUtils.url('Stripe-PaymentElementOrderPlaced'); 110 | } else { 111 | redirectUrl = URLUtils.url('Stripe-PaymentElementOrderPlaced'); 112 | } 113 | } catch (e) { 114 | logStripeErrorMessage('handleAPM: ' + e.message); 115 | 116 | if (sfra) { 117 | redirectUrl = URLUtils.url('Checkout-Begin', 'stage', 'payment', 'apm_return_error', e.message); 118 | } else { 119 | redirectUrl = URLUtils.url('COBilling-Start', 'apm_return_error', e.message); 120 | } 121 | } 122 | 123 | return redirectUrl; 124 | } 125 | 126 | exports.HandleAPM = handleAPM; 127 | exports.HandleAPM.public = true; 128 | -------------------------------------------------------------------------------- /cartridges/int_stripe_core/cartridge/scripts/stripe/helpers/paymentprocessors/stripeCreditHelper.js: -------------------------------------------------------------------------------- 1 | /* eslint-env es6 */ 2 | /* global request */ 3 | 4 | 'use strict'; 5 | 6 | const Transaction = require('dw/system/Transaction'); 7 | const PaymentInstrument = require('dw/order/PaymentInstrument'); 8 | const PaymentMgr = require('dw/order/PaymentMgr'); 9 | const Order = require('dw/order/Order'); 10 | const PaymentTransaction = require('dw/order/PaymentTransaction'); 11 | 12 | /** 13 | * Handle credit card payment 14 | * 15 | * @param {array} args with paramenters 16 | * @returns {array} - array with result info 17 | */ 18 | function Handle(args) { 19 | const checkoutHelper = require('*/cartridge/scripts/stripe/helpers/checkoutHelper'); 20 | const paramsMap = request.httpParameterMap; 21 | const cardType = require('*/cartridge/scripts/stripe/helpers/cardsHelper').getCardType(); 22 | 23 | var prUsed = false; 24 | if (request.httpParameterMap.get('stripe_pr_used').value === 'true') { 25 | prUsed = true; 26 | } 27 | 28 | const params = { 29 | sourceId: paramsMap.stripe_source_id.stringValue, 30 | cardNumber: paramsMap.stripe_card_number.stringValue, 31 | cardHolder: paramsMap.stripe_card_holder.stringValue, 32 | cardType: cardType, 33 | cardExpMonth: paramsMap.stripe_card_expiration_month.stringValue, 34 | cardExpYear: paramsMap.stripe_card_expiration_year.stringValue, 35 | saveCard: paramsMap.stripe_save_card.value, 36 | prUsed: prUsed, 37 | saveGuessCard: paramsMap.stripe_save_guess_card.value 38 | }; 39 | 40 | try { 41 | Transaction.begin(); 42 | checkoutHelper.createStripePaymentInstrument(args.Basket, PaymentInstrument.METHOD_CREDIT_CARD, params); 43 | Transaction.commit(); 44 | return { 45 | success: true 46 | }; 47 | } catch (e) { 48 | Transaction.rollback(); 49 | return { 50 | success: false, 51 | error: true, 52 | errorMessage: e.message 53 | }; 54 | } 55 | } 56 | 57 | /** 58 | * Authorize credit card payment 59 | * 60 | * @param {array} args with paramenters 61 | * @returns {Object} - object with result info 62 | */ 63 | function Authorize(args) { 64 | let responsePayload; 65 | const Site = require('dw/system/Site'); 66 | const orderNo = args.OrderNo; 67 | const paymentInstrument = args.PaymentInstrument; 68 | const paymentIntentId = paymentInstrument.paymentTransaction.getTransactionID(); 69 | const stripeChargeCapture = Site.getCurrent().getCustomPreferenceValue('stripeChargeCapture'); 70 | 71 | /** 72 | * Handle Authorize for zero orders i.e. after full gift cert redeem 73 | */ 74 | if (paymentInstrument && paymentInstrument.paymentTransaction 75 | && paymentInstrument.paymentTransaction.amount 76 | && paymentInstrument.paymentTransaction.amount.value === 0) { 77 | responsePayload = { 78 | authorized: true, 79 | error: false 80 | }; 81 | 82 | return responsePayload; 83 | } 84 | 85 | if (!paymentIntentId) { 86 | /* 87 | * Pass the initial Authorize because SFCC orde is being created before making a call to Sripe to Authorize 88 | */ 89 | responsePayload = { 90 | authorized: true, 91 | error: false 92 | }; 93 | } else { 94 | const stripeService = require('*/cartridge/scripts/stripe/services/stripeService'); 95 | 96 | try { 97 | const paymentIntent = stripeService.paymentIntents.update(paymentIntentId, { 98 | metadata: { 99 | order_id: orderNo, 100 | site_id: Site.getCurrent().getID() 101 | } 102 | }); 103 | 104 | if ((!stripeChargeCapture && paymentIntent.status !== 'requires_payment_method' && paymentIntent.status !== 'requires_capture' && paymentIntent.status !== 'succeeded') || (stripeChargeCapture && paymentIntent.status !== 'succeeded')) { 105 | throw new Error('Payment was not successful, payment intent status is ' + paymentIntent.status); 106 | } 107 | 108 | const charges = paymentIntent.charges; 109 | if (!(charges && charges.total_count && charges.data)) { 110 | throw new Error('Payment was not successful, no charges found'); 111 | } 112 | 113 | const charge = paymentIntent.charges.data[0]; 114 | if (!(charge && charge.id)) { 115 | throw new Error('Payment was not successful, no valid charge found'); 116 | } 117 | 118 | const paymentProcessor = args.PaymentProcessor || PaymentMgr.getPaymentMethod(paymentInstrument.getPaymentMethod()).getPaymentProcessor(); 119 | Transaction.wrap(function () { 120 | paymentInstrument.custom.stripeChargeID = charge.id; 121 | 122 | if (charge.balance_transaction) { 123 | paymentInstrument.paymentTransaction.transactionID = paymentIntentId; 124 | paymentInstrument.paymentTransaction.type = PaymentTransaction.TYPE_CAPTURE; 125 | } 126 | 127 | paymentInstrument.paymentTransaction.paymentProcessor = paymentProcessor; 128 | if (paymentIntent.status === 'succeeded') { 129 | args.Order.setPaymentStatus(Order.PAYMENT_STATUS_PAID); 130 | } 131 | }); 132 | 133 | responsePayload = { 134 | authorized: true, 135 | error: false 136 | }; 137 | } catch (e) { 138 | responsePayload = { 139 | authorized: false, 140 | error: true, 141 | errorMessage: e.message 142 | }; 143 | } 144 | } 145 | 146 | return responsePayload; 147 | } 148 | 149 | exports.Handle = Handle; 150 | exports.Authorize = Authorize; 151 | --------------------------------------------------------------------------------