Spartan Express Checkout
The main purpose of this library is to create an interactive widget for HTML5 based platforms.
Deploy
To update the widget instance at https://spartan-express-checkout.herokuapp.com/, log in to heroku, then:
git push heroku master
Stripe
Test cards
Stripe is integrated on the front end for payment processing. For testing purposes stripe provides the following https://stripe.com/docs/testing#cards.
Apply Pay / Google Pay
Apple pay and Google pay are integrated through stripe. For usage and testing, regardless of development enviroment or production, a https connection is REQUIRED. To produce this on local you will need a tool such as https://ngrok.com/.
For testing on local, you will need to use ngrok to port through to your localhost port through a https connection, and you can then test it there. This will also require you to run npm run server
to start the express server enviroment that emulates the functionality on heroku.
Apple pay
Integrating Apple pay will require you to verify your domain with Apple. Instructions are available https://stripe.com/docs/stripe-js/elements/payment-request-button?html-or-react=react#verifying-your-domain-with-apple-pay.
A domain must be registered through your own stripe dashboard at the following https://dashboard.stripe.com/account/payments/apple_pay.
The domain association file is part of the package installed. If it is not available at /.well-known/apple-developer-merchantid-domain-association
on your own site, please make sure it is hosted there.
Sezzle
To enable sezzle on the widget just set a Sezzle public api key on the configuration object. The widget will display the sezzle checkout option when the property is defined.
// Configuration object
{
...
sezzlePublicKey: "sz_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
...
}
Usage
Configuration
let configuration = {
env: 'development', // Accepted values are: "production" and "development". (Defaults to: "development")
enableStripePaymentElementsFlow: false, // This flag will enable the new Stripe Payment Elements flow
allowPlatformSpecificPayment: false,
api: 'API_URL',
token: 'API_TOKEN',
sezzlePublicKey: "sz_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
// Element represents the element it will render in to if modal is false
widgetElement: {
isModal: false, // True for modal experience (Needs to be opened via a click event), false for in page experience
element: document.querySelector('#widget'),
},
account = {
id: spartanAccount.id,
firstName: spartanAccount.first_name,
lastName: spartanAccount.last_name,
emailAddress: spartanAccount.email,
birthMonth: spartanAccount.birth_date.split('-')[1],
birthDay: spartanAccount.birth_date.split('-')[2],
birthYear: spartanAccount.birth_date.split('-')[0],
gender: spartanAccount.gender,
phoneNumber: spartanAccount.mobile_phone,
tshirtShize: spartanAccount.tshirt_size,
emergencyContactName: spartanAccount.emergency_contact_name,
emergencyContactPhone: spartanAccount.emergency_contact_phone,
},
settings: {
name: 'Spartan',
logo: '',
},
gtm: {
gtmId: 'GTM-T2PRR2V', // Spartan GTM Container, required
auth: 'z_k7asSD5G-X1q_E_y-40g', // optional
preview: 'env-122', // optional
dataLayer: { // optional, anything in this object will be passed through to the dataLayer
country: 'US',
localCurrency: 'USD',
transactionAffiliation: window.location.host
}
},
intl: {
currency: "MXN", // Values follow ISO 4217
language: "en-US",
translations: {} // key-value map
},
theme: {
typography: {
primary: 'Montserrat', // Fonts to Load
primarySource: 'https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap',
secondary: 'Montserrat',
secondarySource: ''
}
},
// Url to assign tickets on completion
assignUrl: 'https://ASSIGN_URL',
// Show questions with tag "member"
showMemberQuestion: true,
// Call back when cart items are updated
onCartItemChange: (item, quantity, eventName) => {
console.log("onCartItemChange", `Quantity ${quantity}`, item);
},
// Call back once purchase is finished
onConfirmation: () => {
},
// Call back to run once purchase is finished and user closes confirmation
onConfirmationClose: () => {
console.log(123)
},
// Call back to run when/if timer timesout
onCheckoutTimeout: () => {
console.log('timeout')
},
// Call back to run when user click on Ice Cream Social button, transactionId can be null
onIceCreamSocialClick: (transactionId) => {
console.log('Ice Cream Social', transactionId);
},
//Image path for background image in Ice Cream Social
iceCreamSocialImagePath: 'https://IMAGE_PATH',
//Show Ice Cream Social block or not
showIceCreamSocial: true,
//TS_org allows to know what rules apply to the price decomposition according to the regulations of each country
TS_org: 'US',
//Refund protect settings
refundProtect: {
//enable or disable refund protection in the widget
enableRefundProtect: true,
//establish location of refund protection source code.
sourceURL: 'https://widget.protectgroup.com/dynamic-widget.js',
//sourceURL: 'https://test.widget.protectgroup.com/dynamic-widget.js',
// * In order to excude a question or ticket type from the order refund a exclusion tag is needed. It can be
// done in the admin panel. By default that tag is 'not-refundable', but can be changed in the constants file.
},
//show GoFundraise block or not
showGoFundraise: true
//If showGoFundraise is true you need to add goFundraiseFundInfo. Use the exact funds names as shown below
goFundraiseFundInfo: {
americanCancerSociety: {
beneficiaryAccountId: '13xxxxx',
eventCampaingId: '1xxxx',
waitForCompletion: true
},
other: {
beneficiaryAccountId: '13xxxxx',
eventCampaingId: '1xxxx',
waitForCompletion: false
}
}
};
Callbacks
onCartItemChange
The callback triggers on each update on the amount of tickets to be purchased and every time a new product is added.
Item schema:
available: boolean,
fees: [{
amount: number;
name: string;
}],
ticketSummary: {
id: string;
name: string;
eventId: string;
description?: string;
minimumAge: number;
maximumAge: number;
requiredGender: GenderType;
},
priceCents: number,
stock: number,
tags: string[],
questions: [{
defaultAnswer: string;
defaultAnswerText: string;
displayAsCheckbox: boolean;
moreInfo: string;
ordering: number;
questionAnswers: [{
addOnAmountCents: number;
answer: string;
id: string;
ordering: number;
oversell: boolean;
quantityAvailable: number;
questionId: string;
showQuantity: boolean;
}];
questionId: string;
questionName: string;
questionText: string;
questionType: {
CHECKBOX = "checkbox",
MULTIPLE = "multiple",
TEXT = "text"
},
required: boolean;
tags: string[];
}],
waveTimes?: [{
waveTimeId: string;
description: string;
remainingQuantity: number;
soldQuantity: number;
startTimestamp: number;
totalQuantity: number;
}]
}
Quantity: selected quantity, number
Event name: Event name, related to the Item property, string.
onConfirmation
The callback is triggered after an order is submitted and a response is retrieved, confirming the payment.
onCartItemChange
The callback is triggered when cart items are updated.
onIceCreamSocialClick
The callback is triggered when the user click on Ice Cream Social button in confirmation page.
Setting the widget checkout tickets
In page
If isModal is set to false, collect the cart details via this format and load them in to the widget using setCheckoutTickets:
const checkoutTickets = [
{
ticketTypeId: '5054',
eventId: '111',
quantity: 1,
},
{
ticketTypeId: '5058',
eventId: '111',
quantity: 3,
},
];
spartanExpressWidget.setCheckoutTickets(checkoutTickets);
In Modal
If isModal is set to true:
TBD
User account
User account fields need to be loaded in to the widget configuration. This must be done on the user end, then attached to the configuration object, and passed in to the widget.
configuration.account = {
firstName: account.first_name,
lastName: account.last_name,
emailAddress: account.email,
birthMonth: account.birth_date.split('-')[1],
birthDay: account.birth_date.split('-')[2],
birthYear: account.birth_date.split('-')[0],
gender: account.gender,
phoneNumber: account.mobile_phone,
};
Script Tag
<script src="/dist/spartan-express-checkout.umd.js"></script>
<div id="widget"></div>
<script>
const spartanExpressWidget = new SpartanExpressCheckout.Widget(configuration);
</script>
ES6 Import
import SpartanExpressCheckout from '@halo-media/spartan-express-checkout';
const spartanExpressWidget = new SpartanExpressCheckout.Widget(configuration);
spartanExpressWidget.setCheckoutTickets(checkoutTickets);