import { CatalogAPI, IfindReference } from '../apis/catalog' import { DEFAULT_CPA, ONECLICK_API_URL } from '../lib/constants' import { IAmount, IFees, IUser as IUserPayment } from '../interfaces/commun' import { IAuthorize, IOrder, IPayment, IProduct, ISession, IUser } from '../interfaces/apis' import { Model, Utils, makeError } from '@oyst/utils' import { isNil, merge, omit, path, pick } from 'ramda' import { IAuthorize } from '@oyst/utils/lib/api/payment' import OrderAPI from '../apis/order' import PaymentAPI from '../apis/payment' import UUID from 'uuid/v1' import UserAPI from '../apis/user' import log from '../lib/log' const { removeUndefinedKeys } = Utils export default class Order extends Model { private authorize: IAuthorize = null private catalogApi: CatalogAPI private cpa: number = null private encryptedCard: string = null private merchantId: string = null private order: IOrder = null private payment: IPayment = null private product: IProduct = null private session: ISession = null private userId: string = null private user: IUser = null private version: number = null private withAuthorize: boolean = true private headers: Object = {} private async getProduct (): Promise { return new Promise(async (resolve, reject) => { try { this.product = await this.catalogApi.findByReferences(merge( pick([ 'product_reference', 'variation_reference' ], this.session), { merchant_id: this.merchantId }) as IfindReference ) resolve(true) } catch (err) { log({ errOrderModelgetProduct: err }) reject(err) } }) } private async getUser () { try { const userId = this.userId ? this.userId : this.session.user.id this.user = await UserAPI.findById(userId) if (!this.user.card) { // If the user has no cards we just throw a 424 so the frontend can shown an "add card form" throw makeError('USER-424-CARD', 'no-card-linked') } if (!this.user.address) { // If the user has no address we just throw a 424 so the frontend can shown an "add address form" throw makeError('USER-424-ADDRESS', 'no-address-linked') } this.user.address = omit(['id', 'created_at'], removeUndefinedKeys(this.user.address)) as any return this } catch (err) { console.error({ errOrderModelgetUser: err, userId: this.userId || this.session.user.id }) throw err } } private getAmount () { const quantity = this.session.quantity as number || 1 const amountWithTaxes = path(['product', 'amount_including_taxes', 'value'], this) as number if (isNil(amountWithTaxes)) { throw makeError('CLICK-404', 'product-not-found') } const amount = { currency: 'EUR', value: amountWithTaxes * quantity } return { amount, quantity } } private getFees (amount: IAmount): IFees { const amountValue = path(['value'], amount) as number if (isNil(amountValue)) { throw makeError('CLICK-404', 'product-not-found') } return { amount: { currency: amount.currency, value: Math.round(amountValue * this.cpa / 10000) }, cpa: this.cpa } } private pay (): Promise { return new Promise(async (resolve, reject) => { try { this.payment = await PaymentAPI.create(this.merchantId, { amount: this.getAmount().amount, cpa: this.product.cpa / 10000, label: `Oyst-${this.product.id}`, notification_url: `${ONECLICK_API_URL}/v1/notifications`, order_id: UUID(), source: 'merchant', user: pick([ 'addresses', 'billing_addresses', 'email', 'first_name', 'id', 'language', 'last_name', 'phone' ], removeUndefinedKeys(this.user)) }) const { email, phone, first_name, last_name } = this.user as IUserPayment this.authorize = await PaymentAPI.authorizeMerchant(this.merchantId, merge({ token: this.payment.token, user: { email, phone } }, this.encryptedCard ? { encrypted_card: this.encryptedCard } : { card: pick(['id', 'preview'], this.user.card) }), this.headers) resolve(true) } catch (err) { log({ errOrderModelPay: err }) reject(err) } }) } private _create (): Promise { return new Promise(async (resolve, reject) => { try { const { quantity, amount } = this.getAmount() const fees = this.getFees(amount) this.order = await OrderAPI.create({ fees, quantity, merchant_id: this.merchantId, order_amount: amount, product: this.product, product_amount: path(['amount_including_taxes'], removeUndefinedKeys(this.product)), product_reference: path(['reference'], removeUndefinedKeys(this.product)), shipment: { amount: { currency: 'EUR', value: 0 }, carrier: 'Oyst', tracking: '' }, transaction: { amount, id: this.authorize.transaction_id }, user: pick([ 'address', 'email', 'first_name', 'id', 'language', 'last_name', 'phone' ], removeUndefinedKeys(this.user)), variation_reference: path([ 'variations', 'reference' ], removeUndefinedKeys(this.product)) }, this.version) resolve(true) } catch (err) { log({ errOrderModel_Create: err }) reject(err) } }) } public create (): Promise { return new Promise(async (resolve, reject) => { try { await this.getProduct() await this.getUser() if (this.withAuthorize) { await this.pay() // Update user from user_id returned by Payment API this.userId = this.authorize.user.id || this.userId await this.getUser() } else { this.authorize = { transaction_id: null } } await this._create() if (this.withAuthorize) { return resolve(merge(this.order, { id: this.order.order.id, product: this.product, user: this.user })) } resolve(merge(this.order, { id: this.order.id, user: this.user })) } catch (err) { log({ errOrderModelCreate: err }) reject(err) } }) } public static delete (id: string, version: number = 1): Promise { return new Promise(async (resolve, reject) => { try { const results = await OrderAPI.delete(id, version) resolve(results) } catch (err) { log({ errOrderModelDelete: err }) reject(err) } }) } constructor (o?: Object) { super() this.init(o) this.cpa = DEFAULT_CPA this.catalogApi = new CatalogAPI() } }