API Reference - SpankPay Developer Documentation

PHOTO EMBED

Sat Sep 10 2022 19:28:12 GMT+0000 (Coordinated Universal Time)

Saved by @JohnnyBGoode

javascript tab="Javascript (manually)"
const crypto = require('crypto')

const crypto = require('crypto')

/**
 * Decodes a SpankPay webhook, returning a triple of:
 *   [data, timestamp, error]
 *
 * Where `data` is the webhook object, and `timestamp` is the
 * call's timestamp (integer seconds since epoch, UTC).
 *
 * If an error is encountered (for example, because the
 * signature is invalid), `error` will be a non-null
 * string describing the error.
 *
 * For example:
 *   const [data, timestamp, error] = decodeSpankPayWebhook(
 *     process.env.SPANKPAY_API_SECRET,
 *     req.headers['x-spankpay-signature'],
 *     req.body,
 *   )
 */
function decodeSpankPayWebhook(secret, sig, data) {
    if (!data || data.slice(0, 1) != '{') {
        const msg = (
            `Empty or non-JSON webhook data: ` +
            JSON.stringify(shorten(data))
        )
        return [null, null, msg]
    }

    const sigData = {}
    sig.split('&').forEach(bit => {
        const [key, val] = bit.split('=')
        sigData[key] = val
    })

    const timestamp = parseInt(sigData.t)
    if (!isFinite(timestamp))
        return [null, null, `Invalid or missing timestamp: ${sig}`]

    const hash = crypto.createHmac('sha256', secret)
    hash.update(`${timestamp}.${data}`)
    const actualSig = hash.digest('hex')
    if (sigData.s !== actualSig)
        return [null, null, `Invalid signature. ${sigData.s} !== ${actualSig}`]

    let dataObj
    try {
        dataObj = JSON.parse(data)
    } catch (e) {
        return [null, null, `Error decoding JSON: ${'' + e}`]
    }

    return [dataObj, timestamp, null]
}

function shorten(s, len) {
    if (!len)
        len = 16

    if (!s || s.length <= len)
        return s

    return s.slice(0, len / 2) + '…' + s.slice(s.length - len / 2)
}

function signSpankPayData(secret, data, t) {
    if (t === undefined)
      t = parseInt(Date.now() / 1000)
    const hash = crypto.createHmac('sha256', secret)
    hash.update(`${t}.${data}`)
    return `t=${t}&s=${hash.digest('hex')}`
}

if (typeof require !== 'undefined' && require.main == module) {
  const secret = 'sk_spankpay'
  const data = '{"SpankPay": "BOOTY"}'
  const sig = signSpankPayData(secret, data, 696969)
  console.log(`Signing '${data}' with secret key '${secret}': ${sig}`)

  examples = [
    ["correctly signed", sig, data],
    ["missing timestamp", "", data],
    ["missing signature", "t=696969", data],
    ["invalid signature", "t=696969&s=invalid", data],
    ["invalid data", sig, '{"invalid": true}'],
    ["empty data", sig, null],
    ["non-JSON data", sig, "invalid"],
  ]
  for (const [name, sig, data] of examples) {
    console.log(`Decoding ${name}:`, decodeSpankPayWebhook(secret, sig, data))
  }
}
content_copyCOPY

https://docs.spankpay.com/api-reference/