diff --git a/dist/TilloProvider-doc.d.ts b/dist/TilloProvider-doc.d.ts new file mode 100644 index 0000000..1be51bb --- /dev/null +++ b/dist/TilloProvider-doc.d.ts @@ -0,0 +1,6 @@ +declare const docs: { + get_info: { + desc: string; + }; +}; +export default docs; diff --git a/dist/TilloProvider-doc.js b/dist/TilloProvider-doc.js new file mode 100644 index 0000000..27cafb6 --- /dev/null +++ b/dist/TilloProvider-doc.js @@ -0,0 +1,13 @@ +"use strict"; +/* Copyright © 2022 Seneca Project Contributors, MIT License. */ +Object.defineProperty(exports, "__esModule", { value: true }); +const docs = { + get_info: { + desc: 'Get information about the Tillo SDK.', + }, +}; +exports.default = docs; +if ('undefined' !== typeof (module)) { + module.exports = docs; +} +//# sourceMappingURL=TilloProvider-doc.js.map \ No newline at end of file diff --git a/dist/TilloProvider-doc.js.map b/dist/TilloProvider-doc.js.map new file mode 100644 index 0000000..1ef2546 --- /dev/null +++ b/dist/TilloProvider-doc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"TilloProvider-doc.js","sourceRoot":"","sources":["../src/TilloProvider-doc.ts"],"names":[],"mappings":";AAAA,gEAAgE;;AAGhE,MAAM,IAAI,GAAG;IAEX,QAAQ,EAAE;QACR,IAAI,EAAE,sCAAsC;KAC7C;CAEF,CAAA;AAED,kBAAe,IAAI,CAAA;AAEnB,IAAI,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE;IACnC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;CACtB"} \ No newline at end of file diff --git a/dist/tillo-provider.d.ts b/dist/tillo-provider.d.ts new file mode 100644 index 0000000..8ae9ea5 --- /dev/null +++ b/dist/tillo-provider.d.ts @@ -0,0 +1,10 @@ +type TilloProviderOptions = { + url: string; + fetch: any; + entity: Record; + debug: boolean; +}; +declare function TilloProvider(this: any, options: TilloProviderOptions): { + exports: {}; +}; +export default TilloProvider; diff --git a/dist/tillo-provider.js b/dist/tillo-provider.js new file mode 100644 index 0000000..f001bfd --- /dev/null +++ b/dist/tillo-provider.js @@ -0,0 +1,152 @@ +"use strict"; +/* Copyright © 2022-2023 Seneca Project Contributors, MIT License. */ +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto = require('crypto'); +const Pkg = require('../package.json'); +function generateAuthSign(signData) { + const sd = new Map(signData); + sd.delete("apiSecret"); + const sdList = []; + sd.forEach((v) => { + sdList.push(v); + }); + return sdList.join('-'); +} +function getAuthSignature(signData) { + const authSign = generateAuthSign(signData); + const hashedSign = crypto.createHmac('sha256', signData.get("apiSecret")) + .update(authSign).digest('hex'); + return hashedSign; +} +function TilloProvider(options) { + const seneca = this; + const makeUtils = this.export('provider/makeUtils'); + const { makeUrl, getJSON, postJSON, entityBuilder } = makeUtils({ + name: 'tillo', + url: options.url, + }); + seneca + .message('sys:provider,provider:tillo,get:info', get_info); + const makeConfig = (config) => seneca.util.deep({ + headers: { + ...seneca.shared.headers + } + }, config); + async function get_info(_msg) { + return { + ok: true, + name: 'tillo', + version: Pkg.version, + }; + } + entityBuilder(this, { + provider: { + name: 'tillo' + }, + entity: { + brand: { + cmd: { + list: { + action: async function (entize, msg) { + const path = "brands"; + const timestamp = new Date().getTime().toString(); + const options = new Map([ + ["apikey", this.shared.headers["API-Key"]], + ["method", "GET"], + ["path", path], + ["timestamp", timestamp], + ["apiSecret", this.shared.secret], + ]); + this.shared.headers.Signature = getAuthSignature(options); + this.shared.headers.Timestamp = timestamp; + this.shared.headers.Accept = "application/json"; + let json = await getJSON(makeUrl(path + "?detail=true", msg.q), makeConfig()); + let brands = json.data.brands; + let list = Object.entries(brands).map(([name, value]) => entize({ name, value })); + return list; + }, + } + } + }, + digitalGC: { + cmd: { + save: { + action: async function (entize, msg) { + var _a, _b; + const timestamp = new Date().getTime().toString(); + const clientRequestId = `${msg.q.user_id}-digitalissue-${timestamp}`; + const brand = msg.q.brand; + const currency = ((_a = msg.q) === null || _a === void 0 ? void 0 : _a.currency) || "GBP"; + const value = msg.q.value; + const sector = ((_b = msg.q) === null || _b === void 0 ? void 0 : _b.sector) || "other"; + const options = new Map([ + ["apikey", this.shared.headers["API-Key"]], + ["method", "POST"], + ["path", "digital-issue"], + ["clientRequestId", clientRequestId], + ["brand", brand], + ["currency", currency], + ["value", value], + ["timestamp", timestamp], + ["apiSecret", this.shared.secret], + ]); + this.shared.headers.Signature = getAuthSignature(options); + this.shared.headers.Timestamp = timestamp; + this.shared.headers.Accept = "application/json"; + let json = await postJSON(makeUrl('digital/issue'), makeConfig({ + body: { + client_request_id: clientRequestId, + brand: brand, + face_value: { + amount: value, + currency, + }, + delivery_method: 'url', + fulfilment_by: 'partner', + sector: sector + } + })); + let order = json; + return entize(order); + }, + } + } + } + } + }); + seneca.prepare(async function () { + let res = await this.post('sys:provider,get:keymap,provider:tillo'); + if (!res.ok) { + throw this.fail('keymap'); + } + this.shared.headers = { + 'API-Key': res.keymap.key.value, + }; + this.shared.secret = res.keymap.secret.value; + }); + return { + exports: {} + }; +} +// Default options. +const defaults = { + // NOTE: include trailing / + url: 'https://sandbox.tillo.dev/api/v2/', + // Use global fetch by default - if exists + fetch: ('undefined' === typeof fetch ? undefined : fetch), + entity: { + order: { + save: { + // Default fields + } + } + }, + // TODO: Enable debug logging + debug: false +}; +Object.assign(TilloProvider, { defaults }); +exports.default = TilloProvider; +if ('undefined' !== typeof (module)) { + module.exports = TilloProvider; +} +//# sourceMappingURL=tillo-provider.js.map \ No newline at end of file diff --git a/dist/tillo-provider.js.map b/dist/tillo-provider.js.map new file mode 100644 index 0000000..979c5e3 --- /dev/null +++ b/dist/tillo-provider.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tillo-provider.js","sourceRoot":"","sources":["../src/tillo-provider.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index c29e513..0f4096e 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "peerDependencies": { "@seneca/env": ">=0.2", "@seneca/provider": ">=1.0", - "seneca": ">=3", + "seneca": ">=3||>=4.0.0-rc2", "seneca-entity": ">=19", "seneca-promisify": ">=3" }, diff --git a/src/tillo-provider.ts b/src/tillo-provider.ts index f7849ca..50dde6f 100644 --- a/src/tillo-provider.ts +++ b/src/tillo-provider.ts @@ -12,18 +12,24 @@ type TilloProviderOptions = { debug: boolean } -type TilloSignatureOptions = { - apiKey: string - apiSecret: string - method: string - path?: string - timestamp: string +function generateAuthSign(signData: Map) { + const sd = new Map(signData) + sd.delete("apiSecret") + const sdList: any = [] + + + sd.forEach((v: any) => { + sdList.push(v) + }) + + return sdList.join('-') } +function getAuthSignature(signData: Map) { + const authSign = generateAuthSign(signData) -function getAuthSignature(signData: TilloSignatureOptions) { - const authSign = `${signData.apiKey}-${signData.method}-${signData.path}-${signData.timestamp}` - const hashedSign = crypto.createHmac('sha256', signData.apiSecret).update(authSign).digest('hex') + const hashedSign = crypto.createHmac('sha256', signData.get("apiSecret")) + .update(authSign).digest('hex') return hashedSign } @@ -69,39 +75,27 @@ function TilloProvider(this: any, options: TilloProviderOptions) { name: 'tillo' }, entity: { - customer: { - cmd: { - list: { - action: async function(this: any, entize: any, msg: any) { - let json: any = - await getJSON(makeUrl('customers', msg.q), makeConfig()) - let customers = json - let list = customers.map((data: any) => entize(data)) - return list - }, - } - } - }, brand: { cmd: { list: { action: async function(this: any, entize: any, msg: any) { const path = "brands" const timestamp = new Date().getTime().toString() - const options: TilloSignatureOptions = { - apiKey: this.shared.headers["API-Key"], - method: "GET", - path, - timestamp, - apiSecret: this.shared.secret - } + + const options: Map = new Map([ + ["apikey", this.shared.headers["API-Key"]], + ["method", "GET"], + ["path", path], + ["timestamp", timestamp], + ["apiSecret", this.shared.secret], + ]) this.shared.headers.Signature = getAuthSignature(options) this.shared.headers.Timestamp = timestamp this.shared.headers.Accept = "application/json" let json: any = - await getJSON(makeUrl(path, msg.q), makeConfig()) + await getJSON(makeUrl(path + "?detail=true", msg.q), makeConfig()) let brands = json.data.brands let list = Object.entries(brands).map(([name, value]: any) => entize({ name, value })) return list @@ -109,36 +103,49 @@ function TilloProvider(this: any, options: TilloProviderOptions) { } } }, - order: { + digitalGC: { cmd: { - list: { - action: async function(this: any, entize: any, msg: any) { - let json: any = - await getJSON(makeUrl('orders', msg.q), makeConfig()) - let orders = json.orders - let list = orders.map((data: any) => entize(data)) - - // TODO: ensure seneca-transport preserves array props - list.page = json.page - - return list - }, - }, save: { action: async function(this: any, entize: any, msg: any) { - let body = this.util.deep( - this.shared.primary, - options.entity.order.save, - msg.ent.data$(false) - ) + const timestamp = new Date().getTime().toString() + const clientRequestId = `${msg.q.user_id}-digitalissue-${timestamp}` + const brand = msg.q.brand + const currency = msg.q?.currency || "GBP" + const value = msg.q.value + const sector = msg.q?.sector || "other" + + const options: Map = new Map([ + ["apikey", this.shared.headers["API-Key"]], + ["method", "POST"], + ["path", "digital-issue"], + ["clientRequestId", clientRequestId], + ["brand", brand], + ["currency", currency], + ["value", value], + ["timestamp", timestamp], + ["apiSecret", this.shared.secret], + ]) + + this.shared.headers.Signature = getAuthSignature(options) + this.shared.headers.Timestamp = timestamp + this.shared.headers.Accept = "application/json" let json: any = - await postJSON(makeUrl('orders', msg.q), makeConfig({ - body + await postJSON(makeUrl('digital/issue'), makeConfig({ + body: { + client_request_id: clientRequestId, + brand: brand, + face_value: { + amount: value, + currency, + }, + delivery_method: 'url', + fulfilment_by: 'partner', + sector: sector + } })) let order = json - order.id = order.referenceOrderID return entize(order) }, } diff --git a/test/tillo-provider.test.ts b/test/tillo-provider.test.ts index 29a3978..b99ba60 100644 --- a/test/tillo-provider.test.ts +++ b/test/tillo-provider.test.ts @@ -53,6 +53,20 @@ describe('tillo-provider', () => { // expect(list.length > 0).toBeTruthy() // }) + // test('issue-gc', async () => { + // if (!Config) return; + // const seneca = await makeSeneca() + // + // const redeemTemplate = await seneca.entity("provider/tillo/digitalGC").save$({ + // user_id: "user01", + // brand: "hobbycraft", + // value: 10.00, + // }) + // console.log('REDEEM TEMPLATE ', redeemTemplate) + // + // expect(redeemTemplate).toBeTruthy() + // }) + })