Last active
October 22, 2024 11:32
-
-
Save caub/0999d3a139d44efd6609fb110eff23b1 to your computer and use it in GitHub Desktop.
Storeganise custom billing / custom payment gateway addons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import crypto from 'crypto'; | |
export default async (req, res) => { | |
try { | |
if (req.method !== 'POST') { | |
return res.status(404).send(); | |
} | |
if (req.path.endsWith('/webhook')) { | |
console.log('webhook', req.body); | |
// Here you could potentially handle async webhooks used by the remote payment gateway API | |
return res.send(); | |
} | |
// Verify request's signature, to ensure it comes from Storeganise | |
if (req.headers['Sg-Signature'] !== crypto.createHmac('sha256', process.env.SG_API_SECRET).update(JSON.stringify(req.body)).digest('base64')) { | |
res.status(401).send(); | |
} | |
const { data, type, businessCode, apiUrl, addonId } = req.body; | |
const api = storeganiseApi({ apiUrl, addonId }); // See example of storeganiseApi helper: https://gist.github.com/caub/14439d39ef9adf14bdd5dc25a402ce18 | |
const addon = await api.get(`addons/${addonId}`); // If necessary fetch addon, as that's a good place to store credentials for the remote payment API | |
const user = await api.get(`users/${data.userId}`, { include: 'customFields' }); // load target user | |
const payApi = customPaymentApi(addon); // Initialize your own custom payment API, using addon.customFields credentials if needed | |
// Handles the 3 main events: | |
switch (type) { | |
case 'billing.list': { | |
if (!user.customFields.pay_token) return res.json([]); // for example store your remote payment customerId as pay_token user custom field | |
try { | |
const data = await payApi.retrievePaymentMethods({ token: user.customFields.bpoint_token }); | |
return res.json(data); | |
// the format of the response should be an array of object with either a `card` or `bank` property | |
// example: | |
/* | |
[{ | |
"card": { | |
"number": "411111...111", | |
"expiry": { | |
"month": "99", | |
"year": "00" | |
}, | |
"name": "Cyril Testing", | |
"scheme": "Visa", | |
"localisation": "International", | |
"type": "Debit" | |
} | |
}] | |
*/ | |
} catch (err) { | |
return res.status(400).json({ message: err.message }); | |
} | |
} | |
case 'billing.charge': { | |
// data.amount can be negative for refunds, usually you'll need to handle these separately: if (data.amount < 0) { .. } | |
if (!user.customFields.bpoint_token) { | |
return res.status(204).send(); | |
} | |
// Process charge using payApi | |
const txn = await payApi.createPayment({ | |
token: user.customFields.bpoint_token, | |
amount: Math.round(data.amount * 100), // in cents (often needed), as data.amount is in main currency unit, not cents | |
currency: addon.customFields.pay_currency, // example use of addon custom fields | |
}); | |
// Send response in this format: | |
return res.send({ | |
id: txn.txnNumber, | |
amount: txn.amount, | |
status: txn.responseCode === '0' ? 'succeeded' : txn.responseCode === '1' ? 'processing' : 'failed', | |
currency: txn.currency, // optional | |
paymentMethod: txn.paymentMethod, // optional | |
isTest: txn.isTestTxn, // optional | |
}); | |
} | |
case 'billing.checkout': { | |
// Here you need to render or redirect to a custom checkout form, where customer will enter their card or bank account details | |
return res.send(`<!doctype html> | |
<html> | |
<head> | |
<title>BPOINT Payment</title> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width,initial-scale=1"> | |
</head> | |
<body> | |
<h1>Pay with {{customBilling}}</h1> | |
<form> | |
... | |
</form> | |
</body> | |
</html>`); | |
} | |
default: | |
return res.status(404).send(`Unknown event type ${type}`); | |
} | |
} catch (err) { | |
console.log(err); | |
res.status(400).send(err.message); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment