Update Helcim webhook handling for better transaction reliability (issue #1)

This commit is contained in:
Skylar Ittner 2026-03-31 17:41:50 -06:00
parent 37c8672e4a
commit ee989accee
4 changed files with 33 additions and 12 deletions

View File

@ -9,7 +9,7 @@ rm Helcim.zip
cd build
zip -r ../Helcim.v0.0.1.zip Helcim
zip -r ../Helcim.v0.0.2.zip Helcim
cd ..

View File

@ -1,6 +1,6 @@
{
"name": "postalpoint_helcim_plugin",
"version": "0.0.1",
"version": "0.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {

View File

@ -1,6 +1,6 @@
{
"name": "postalpoint_helcim_plugin",
"version": "0.0.1",
"version": "0.0.2",
"main": "plugin.js",
"author": "PostalPortal LLC",
"license": "BSD-3-Clause",

View File

@ -116,6 +116,10 @@ function getCardBrand(cardType) {
return cardType;
}
var currentReceiptID = null;
var checkoutCancelled = false;
exports.init = function () {
global.apis.pos.registerCardProcessor({
name: "Helcim",
@ -133,12 +137,15 @@ exports.init = function () {
// it is assumed the payment was successful.
const code = global.apis.settings.get(PLUGINID + ".devicecode", "");
const receiptID = global.apis.pos.getReceiptID();
checkoutCancelled = false;
currentReceiptID = receiptID;
try {
var transactionAmount = global.apis.i18n.moneyToFixed(amount / 100.0) * 1.0;
// 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
var purchaseResp = await apiRequest(`devices/${code}/payment/purchase`, {
currency: global.apis.i18n.currency().toUpperCase(),
transactionAmount: global.apis.i18n.moneyToFixed(amount / 100.0) * 1.0,
transactionAmount: transactionAmount,
invoiceNumber: receiptID
}, "POST", "text");
if (purchaseResp.length > 0) {
@ -151,18 +158,28 @@ exports.init = function () {
}
var paymentID = "";
var purchaseResp;
while (paymentID == "") {
if (checkoutCancelled) {
checkoutCancelled = false;
global.apis.pos.addOnscreenPaymentLog("Checkout cancelled. Warning: It's still possible for the payment to succeed on the reader but not be recognized in PostalPoint; cancel it on the reader too.");
return false;
}
await global.apis.util.delay(1000); // Wait a second
try {
var pollResults = await global.apis.util.http.webhook.poll(WEBHOOK_SOURCE);
for (var i = 0; i < pollResults.length; i++) {
var resultBody = JSON.parse(pollResults[i].body);
if (resultBody.type == "cardTransaction") {
paymentID = resultBody.id;
purchaseResp = await apiRequest(`card-transactions/${resultBody.id}`, null, "GET");
global.apis.util.http.webhook.ack(pollResults[i].id);
if (purchaseResp.invoiceNumber == currentReceiptID) {
console.log("Got transaction ID from Helcim webhook:", paymentID);
console.log("purchaseResp", purchaseResp);
paymentID = resultBody.id;
}
} else if (resultBody.type == "terminalCancel") {
if (resultBody.data.deviceCode == code && resultBody.data.invoiceNumber == receiptID) {
if (resultBody.data.deviceCode == code && resultBody.data.invoiceNumber == currentReceiptID && resultBody.data.transactionAmount == transactionAmount) {
paymentID = "CANCEL";
global.apis.util.http.webhook.ack(pollResults[i].id);
}
@ -174,18 +191,17 @@ exports.init = function () {
}
if (paymentID == "CANCEL") {
global.apis.pos.addOnscreenPaymentLog("Helcim payment not completed.");
global.apis.alert("The card payment was canceled.", "Payment canceled");
global.apis.pos.addOnscreenPaymentLog("Helcim payment not completed: cancelled on the card terminal.");
global.apis.alert("The card payment was cancelled on the card terminal.", "Payment cancelled");
currentReceiptID = null;
return false;
}
console.log("Got transaction ID from Helcim webhook:", paymentID);
purchaseResp = await apiRequest(`card-transactions/${paymentID}`, null, "GET");
console.log("purchaseResp", purchaseResp);
if (typeof purchaseResp?.errors != "undefined" && purchaseResp.errors.length > 0) {
global.apis.pos.addOnscreenPaymentLog("Helcim card payment error: " + purchaseResp.errors[0]);
global.apis.alert("Could not finish card payment: " + purchaseResp.errors[0], "Card Payment Error");
currentReceiptID = null;
return false;
}
@ -200,6 +216,7 @@ exports.init = function () {
pstring
)
);
currentReceiptID = null;
return true;
}
} else if (purchaseResp.status == "DECLINED") {
@ -209,6 +226,7 @@ exports.init = function () {
} else {
global.apis.alert("The customer's card was declined.", "Card Error");
}
currentReceiptID = null;
return false;
}
@ -220,6 +238,7 @@ exports.init = function () {
global.apis.alert("The transaction didn't complete correctly (Helcim card plugin reached an undefined state)", "Card Error");
}
currentReceiptID = null;
return false;
} catch (ex) {
global.apis.pos.addOnscreenPaymentLog(`Error: ${ex.message}`);
@ -235,6 +254,8 @@ exports.init = function () {
},
cancelCheckout: async function () {
// The user requested to cancel the payment.
// Stop the webhook wait loop.
checkoutCancelled = true;
},
finishPayment: async function ( {checkoutResponse}) {
// Finish a payment that was authorized but not captured because checkout was called with capture = false