237 lines
12 KiB
JavaScript
237 lines
12 KiB
JavaScript
// This is a sample PostalPoint plugin that adds a card payment processor.
|
|
|
|
exports.init = function () {
|
|
global.apis.pos.registerCardProcessor({
|
|
name: "Demo Card Processor",
|
|
init: async function () {
|
|
// This function runs once after starting PostalPoint
|
|
// and before any other card processor functions are called.
|
|
},
|
|
checkout: async function({amount, capture = true}) {
|
|
// amount is an integer number of pennies.
|
|
|
|
// If an error is encountered during processing,
|
|
// display an error message in a dialog and return boolean false.
|
|
// If this function returns anything except false or undefined, and doesn't throw an error,
|
|
// it is assumed the payment was successful.
|
|
try {
|
|
if (capture) {
|
|
// authorize, capture, add a ReceiptPayment to the receipt, and return boolean true.
|
|
global.apis.pos.addOnscreenPaymentLog("Getting card payment..."); // Add a line to the onscreen card processing status log
|
|
await global.apis.util.delay(1000); // Replace this with something useful!
|
|
global.apis.pos.addReceiptPayment(
|
|
new global.apis.pos.ReceiptPayment(
|
|
(amount / 100).toFixed(2) * 1,
|
|
"card", // Payment type. Accepted values are card, ach, crypto, cash, check, account, and free. Other types will be displayed as-is to the user and on the receipt.
|
|
"Demo Card\nCardholder Name, etc\nMore info for receipt" // Additional text for receipt
|
|
)
|
|
);
|
|
global.apis.pos.addOnscreenPaymentLog("Payment successful!");
|
|
return true;
|
|
} else {
|
|
// only authorize the payment, don't actually capture/charge the payment method,
|
|
// and return whatever transaction data that will be passed to finishPayment to capture the payment.
|
|
await global.apis.util.delay(1000); // Replace this with something useful!
|
|
return {amount: amount};
|
|
}
|
|
} catch (ex) {
|
|
global.apis.pos.addOnscreenPaymentLog(`Error: ${ex.message} [okay to put extra details here for troubleshooting or tech support, it's visible to the cashier]`);
|
|
if (global.apis.kiosk.isKiosk()) {
|
|
// This message will be shown to an end-user/customer, not a cashier/employee
|
|
global.apis.alert("Your card was declined.", "Card Error");
|
|
} else {
|
|
global.apis.alert("The customer's card was declined.", "Card Error");
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
cancelCheckout: function () {
|
|
// The user requested to cancel the payment.
|
|
// Reset the terminal to its resting state, clear its screen, etc.
|
|
},
|
|
finishPayment: async function ({checkoutResponse}) {
|
|
// Finish a payment that was authorized but not captured because checkout was called with capture = false
|
|
// If payment was already captured and added to the receipt for some reason, just return true.
|
|
await global.apis.util.delay(1000); // Replace this with something useful!
|
|
global.apis.pos.addReceiptPayment(
|
|
new global.apis.pos.ReceiptPayment(
|
|
(checkoutResponse.amount / 100).toFixed(2) * 1,
|
|
"card", // Payment type.
|
|
"Demo Card\nCardholder Name, etc\nMore info for receipt" // Additional text for receipt
|
|
)
|
|
);
|
|
return true;
|
|
},
|
|
updateCartDisplay: function (receipt) {
|
|
// Show transaction data on the card reader display.
|
|
// This function will be called when the cart or total changes.
|
|
console.log(receipt);
|
|
// Sample structure of the receipt variable:
|
|
receipt = {
|
|
"items": [
|
|
{
|
|
"id": "testitem",
|
|
"label": "Test Item",
|
|
"text": "",
|
|
"priceEach": 2,
|
|
"qty": 1,
|
|
"cost": 0,
|
|
"retail": 2,
|
|
"taxRate": 0.1,
|
|
"free": false,
|
|
"barcode": "",
|
|
"certifiedInfo": false,
|
|
"isMerch": true,
|
|
"surcharge": false
|
|
},
|
|
{
|
|
"id": "9100123456789012345678",
|
|
"label": "Test Package",
|
|
"text": "Package Details\nTracking # 9100 1234 5678 9012 3456 78\nTo:\nTEST PERSON\nORGANIZATION INC\n123 TEST ROAD\nTESTTOWN TE 99999-0001",
|
|
"priceEach": 8,
|
|
"qty": 1,
|
|
"cost": 0,
|
|
"retail": 8,
|
|
"taxRate": 0,
|
|
"free": false,
|
|
"barcode": "9100123456789012345678",
|
|
"certifiedInfo": false,
|
|
"isMerch": false,
|
|
"surcharge": false,
|
|
"toAddress": {
|
|
"name": "TEST PERSON",
|
|
"company": "ORGANIZATION INC",
|
|
"street1": "123 TEST ROAD",
|
|
"street2": null,
|
|
"city": "TESTTOWN",
|
|
"state": "TE",
|
|
"zip": "99999-0001",
|
|
"email": null,
|
|
"phone": null,
|
|
"country": "US"
|
|
},
|
|
"fromAddress": {
|
|
"name": "TEST PERSON",
|
|
"company": "ORGANIZATION INC",
|
|
"street1": "123 TEST ROAD",
|
|
"street2": null,
|
|
"city": "TESTTOWN",
|
|
"state": "TE",
|
|
"zip": "99999-0001",
|
|
"email": null,
|
|
"phone": null,
|
|
"country": "US"
|
|
}
|
|
}
|
|
],
|
|
"payments": [
|
|
{
|
|
"amount": 10,
|
|
"amountFormatted": "$10.00",
|
|
"type": "cash",
|
|
"label": "Cash",
|
|
"text": "",
|
|
"texthtml": "",
|
|
"id": "12345678_cash_10"
|
|
},
|
|
{
|
|
"amount": 12.34,
|
|
"amountFormatted": "$12.34",
|
|
"type": "card",
|
|
"label": "Card",
|
|
"text": "Card Details here\n1234abcd",
|
|
"texthtml": "Card Details here<br />1234abcd",
|
|
"id": "87654321_card_12.34"
|
|
}
|
|
],
|
|
"subtotal": 10,
|
|
"subtotalFormatted": "$10.00",
|
|
"tax": 0.2,
|
|
"taxFormatted": "$0.20",
|
|
"grandTotal": 10.2,
|
|
"grandTotalFormatted": "$10.20",
|
|
"paid": 22.34,
|
|
"paidFormatted": "$22.34",
|
|
"due": -12.14, // If negative, is the amount of change owed to the customer instead
|
|
"dueFormatted": "$12.14"
|
|
}
|
|
},
|
|
checkoutSavedMethod: async function ({customerID, paymentMethodID, amount}) {
|
|
// Same as checkout() except using a payment method already on file.
|
|
// customerID and paymentMethodID are provided by getSavedPaymentMethods below.
|
|
await global.apis.util.delay(1000); // Replace this with something useful!
|
|
global.apis.pos.addReceiptPayment(
|
|
new global.apis.pos.ReceiptPayment(
|
|
(amount / 100).toFixed(2) * 1,
|
|
"card", // Payment type.
|
|
"Card on File\nx1234" // Additional text for receipt
|
|
)
|
|
);
|
|
return true;
|
|
},
|
|
saveCardForOfflineUse: async function ({statusCallback, customerUUID, name, company, street1, street2, city, state, zip, country, email, phone}) {
|
|
// Use the card reader to capture an in-person card and save it for offline use.
|
|
// Provided details are the customer's info, which might be empty strings except for the customerUUID.
|
|
// Saved card details must be tied to the customerUUID, as that's how saved cards are looked up.
|
|
|
|
// statusCallback(string, boolean) updates the progress message on the cashier's screen.
|
|
// If the boolean is true, the progress message is replaced with a confirmation message.
|
|
statusCallback("Insert the card into the reader.", false);
|
|
|
|
await global.apis.util.delay(1000); // Wait for the customer to insert their card,
|
|
//then save it for later offline billing
|
|
|
|
statusCallback("Saving card details...", false);
|
|
|
|
await global.apis.util.delay(1000);
|
|
|
|
statusCallback("Card saved!", true);
|
|
|
|
return true; // Card saved to customer
|
|
// If an error occurred, you can throw it and the error message will be displayed to the cashier.
|
|
// Alternatively, return boolean false and display the error yourself with global.apis.alert(message, title) or something.
|
|
},
|
|
cancelSaveCardForOfflineUse: function () {
|
|
// Cancel the process running in saveCardForOfflineUse() at the user/cashier's request.
|
|
},
|
|
getSavedPaymentMethods: async function ({customerUUID}) {
|
|
// Return all saved payment methods tied to the provided customer UUID.
|
|
var methods = [];
|
|
methods.push({
|
|
customer: "<internal string referencing the customer>", // Passed to checkoutSavedMethod as customerID
|
|
customer_uuid: customerUUID,
|
|
id: "<card/payment method identifier>", // Passed to checkoutSavedMethod as paymentMethodID
|
|
type: "card", // Payment type. Accepted values are card, ach, crypto, cash, check, account, and free.
|
|
label: "Visa debit x1234 (exp. 12/29)", // Label for payment method
|
|
label_short: "Visa debit x1234" // Abbreviated label for payment method
|
|
});
|
|
return methods;
|
|
},
|
|
deleteSavedPaymentMethod: async function ({customerUUID, customerID, paymentMethodID}) {
|
|
// Delete the payment method identified by paymentMethodID and tied to the PostalPoint customerUUID and the card processor customerID.
|
|
// If unable to delete, throw an error and the error message will be displayed to the cashier.
|
|
await global.apis.util.delay(1000);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Plugin settings to display.
|
|
exports.config = [
|
|
{
|
|
type: "password",
|
|
key: "democardprocessor_apikey",
|
|
defaultVal: "",
|
|
label: "API Key",
|
|
placeholder: "",
|
|
text: "API Key"
|
|
},
|
|
{
|
|
type: "button",
|
|
label: "Test Button",
|
|
text: "Some text about the button",
|
|
onClick: function () {
|
|
global.apis.ui.openSystemWebBrowser("https://postalpoint.app");
|
|
}
|
|
}
|
|
];
|