import * as IMerchant from '../interfaces/merchant' import { Database, Model, i18n as translation } from '@oyst/utils' import { toCamel, toSnake } from 'convert-keys' import Boom from 'boom' import JWT from 'jsonwebtoken' import Url from './url' import { TOKEN_EXPIRATION as expiresIn } from '../lib/constants' const DB = new Database('merchants') const checkTokenWithKey = (token: string, key: string): Promise => new Promise( (resolve, reject) => { JWT.verify(token, key, (err, data) => { if (err) { return reject(err) } resolve(data.data) }) } ) export default class Merchant extends Model { private url: Url = null public createdAt: Date = null public firstKey: string = null public firstTtl: Date = null public id: string = null public secondKey: string = null public secondTtl: Date = null public updatedAt: Date = null public static findOrCreate (obj: IMerchant.ICreate): Promise { return new Promise(async (resolve, reject) => { try { resolve(await Merchant.find(obj.id)) } catch (err) { if (err.isBoom && err.output.statusCode === 404) { try { return resolve(await Merchant.create({ id: obj.id })) } catch (e) { return reject(e) } } reject(err) } }) } public static find (id: string): Promise { return new Promise(async (resolve, reject) => { try { const merchant = await DB.find({ id }) if (!merchant || !merchant.length) { return reject(Boom.notFound(translation.i18n.__('merchant-not-found'))) } resolve(new Merchant(toCamel(merchant[0]))) } catch (err) { reject(err) } }) } public static create (obj: IMerchant.ICreate): Promise { return new Promise(async (resolve, reject) => { try { const merchant = await DB.insert(toSnake(obj)) if (!merchant) { return reject(Boom.badImplementation(translation.i18n.__('merchant-not-created'))) } resolve(new Merchant(toCamel(merchant[0]))) } catch (err) { reject(err) } }) } constructor (o?: Partial) { super() this.init(o) this.url = new Url({ merchantID: this.id }) } private keys (): Promise { return new Promise((resolve, reject) => { DB.massive.get_merchant_secret([this.id], (err, keys) => { if (err || !keys) { return reject(err || Boom.badImplementation(translation.i18n.__('Secret keys not found'))) } this.init(toCamel(keys[0])) resolve(toCamel(keys[0])) }) }) } public renewKeys (): Promise { return new Promise((resolve, reject) => { DB.massive.renewMerchantSecret([this.id], async (err, keys) => { if (err || !keys) { return reject(err || Boom.badImplementation(translation.i18n.i18n.__('Secret keys not found'))) } this.init(toCamel(keys[0])) resolve(true) }) }) } public rotateKeys (): Promise { return new Promise((resolve, reject) => { DB.massive.rotateMerchantSecret([this.id], async (err, keys) => { if (err || !keys) { return reject(err || Boom.badImplementation(translation.i18n.i18n.__('Secret keys not found'))) } this.init(toCamel(keys[0])) resolve(true) }) }) } public sign (o: any): Promise { return new Promise(async (resolve, reject) => { try { const data = { data: o } JWT.sign(data, (await this.keys()).secondKey, { expiresIn }, (err, token) => { if (err) { return reject(err) } this.url.token = token resolve(token) }) } catch (err) { reject(err) } }) } public verify (token: string): Promise { return new Promise(async (resolve, reject) => { try { await this.keys() resolve(await checkTokenWithKey(token, this.secondKey)) } catch (err) { if (!(err.name === 'JsonWebTokenError' && err.message === 'invalid signature')) { return reject(err) } try { resolve(await checkTokenWithKey(token, this.firstKey)) } catch (e) { reject(e) } } }) } public getUrl (version: number = 1): string { this.url.version = version return this.url.encode() } public getTTL (): Object { return { fist: this.firstTtl, second: this.secondTtl } } public get $url (): Url { return this.url } }