trailpack-proxy-cart
Version:
eCommerce - Trailpack for Proxy Engine
479 lines (444 loc) • 18.1 kB
JavaScript
/* eslint no-console: [0] */
'use strict'
const Service = require('trails/service')
const Errors = require('proxy-engine-errors')
const TRANSACTION_STATUS = require('../../lib').Enums.TRANSACTION_STATUS
const TRANSACTION_KIND = require('../../lib').Enums.TRANSACTION_KIND
/**
* @module PaymentService
* @description Payment Service
*/
module.exports = class PaymentService extends Service {
/**
* Authorizes and amount
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
authorize(transaction, options){
options = options || {}
transaction.description = transaction.description || 'Transaction Authorize'
const Transaction = this.app.orm.Transaction
const paymentProcessor = this.app.config.proxyGenerics[transaction.gateway]
|| this.app.config.get('proxyGenerics.payment_processor')
if (!(transaction instanceof Transaction)){
throw new Error('Transaction must be an instance')
}
if (!paymentProcessor || !paymentProcessor.adapter) {
const err = new Error('Payment Processor is unspecified')
return Promise.reject(err)
}
let resTransaction
return this.app.services.PaymentGenericService.authorize(transaction, paymentProcessor)
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('PaymentGenericService.authorize did not return Transaction instance')
}
return _transaction.save({transaction: options.transaction || null})
})
.then(_transaction => {
resTransaction = _transaction
const event = {
object_id: resTransaction.order_id,
object: 'order',
objects: [{
order: resTransaction.order_id
},{
transaction: resTransaction.id
}],
type: `order.transaction.authorize.${resTransaction.status}`,
message: `Order ID ${ resTransaction.order_id} transaction authorize of ${ this.app.services.ProxyCartService.formatCurrency(resTransaction.amount,resTransaction.currency)} ${resTransaction.currency} ${resTransaction.status}`,
data: resTransaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return resTransaction
})
}
/**
* Captures and Authorized Amount
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
capture(transaction, options){
options = options || {}
transaction.description = transaction.description || 'Transaction Capture'
const Transaction = this.app.orm['Transaction']
const paymentProcessor = this.app.config.proxyGenerics[transaction.gateway]
|| this.app.config.get('proxyGenerics.payment_processor')
if (!paymentProcessor || !paymentProcessor.adapter) {
const err = new Error('Payment Processor is unspecified')
return Promise.reject(err)
}
let resTransaction
// Resolve the authorized transaction
return Transaction.resolve(transaction, {transaction: options.transaction || null })
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('Did not resolve a Transaction instance')
}
if (_transaction.kind !== TRANSACTION_KIND.AUTHORIZE) {
throw new Error(`Transaction kind must be '${TRANSACTION_KIND.AUTHORIZE}' to be captured`)
}
if (_transaction.status !== TRANSACTION_STATUS.SUCCESS) {
throw new Error(`Transaction status must be '${TRANSACTION_STATUS.SUCCESS}' to be captured`)
}
return this.app.services.PaymentGenericService.capture(_transaction, paymentProcessor)
})
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('PaymentGenericService.capture did not return Transaction instance')
}
return _transaction.save({transaction: options.transaction || null})
})
.then(_transaction => {
resTransaction = _transaction
const event = {
object_id: resTransaction.order_id,
object: 'order',
objects: [{
order: resTransaction.order_id
},{
transaction: resTransaction.id
}],
type: `order.transaction.capture.${resTransaction.status}`,
message: `Order ID ${resTransaction.order_id} transaction capture of ${ this.app.services.ProxyCartService.formatCurrency(resTransaction.amount,resTransaction.currency)} ${resTransaction.currency} ${resTransaction.status}`,
data: resTransaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return resTransaction
})
}
/**
* Authorizes and Captures an amount
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
sale(transaction, options){
options = options || {}
transaction.description = transaction.description || 'Transaction Sale'
const Transaction = this.app.orm.Transaction
const paymentProcessor = this.app.config.proxyGenerics[transaction.gateway]
|| this.app.config.get('proxyGenerics.payment_processor')
if (!paymentProcessor || !paymentProcessor.adapter) {
const err = new Error('Payment Processor is unspecified')
return Promise.reject(err)
}
let resTransaction
// Resolve the authorized transaction
return Transaction.resolve(transaction, {transaction: options.transaction || null })
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('Did not resolve a Transaction instance')
}
return this.app.services.PaymentGenericService.sale(_transaction, paymentProcessor)
})
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('PaymentGenericService.sale did not return Transaction instance')
}
return _transaction.save({transaction: options.transaction || null})
})
.then(_transaction => {
resTransaction = _transaction
const event = {
object_id: resTransaction.order_id,
object: 'order',
objects: [{
order: resTransaction.order_id
},{
transaction: resTransaction.id
}],
type: `order.transaction.sale.${resTransaction.status}`,
message: `Order ID ${resTransaction.order_id} transaction sale of ${ this.app.services.ProxyCartService.formatCurrency(resTransaction.amount,resTransaction.currency)} ${resTransaction.currency} ${resTransaction.status}`,
data: resTransaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return resTransaction
})
}
/**
* Returns a pending transaction (No 3rd party Transaction Created Yet)
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
manual(transaction, options){
options = options || {}
const Transaction = this.app.orm.Transaction
if (!(transaction instanceof Transaction)){
throw new Error('Transaction must be an instance')
}
transaction.status = TRANSACTION_STATUS.PENDING
return transaction.save({transaction: options.transaction || null})
.then(() => {
const event = {
object_id: transaction.order_id,
object: 'order',
objects: [{
order: transaction.order_id
},{
transaction: transaction.id
}],
type: `order.transaction.${transaction.kind}.${transaction.status}`,
message: `Order ID ${transaction.order_id} transaction ID ${ transaction.id } ${transaction.kind} of ${ this.app.services.ProxyCartService.formatCurrency(transaction.amount,transaction.currency)} ${transaction.currency} ${transaction.status}`,
data: transaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return transaction
})
}
/**
*
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
void(transaction, options){
options = options || {}
const Transaction = this.app.orm['Transaction']
const paymentProcessor = this.app.config.proxyGenerics[transaction.gateway]
|| this.app.config.get('proxyGenerics.payment_processor')
if (!paymentProcessor || !paymentProcessor.adapter) {
const err = new Error('Payment Processor is unspecified')
return Promise.reject(err)
}
transaction.description = transaction.description || 'Transaction Void'
let resTransaction
return Transaction.resolve(transaction, {transaction: options.transaction || null })
.then(_transaction => {
if (!_transaction) {
throw new Errors.FoundError(Error('Transaction Not Found'))
}
if (!(_transaction instanceof Transaction)) {
throw new Error('Did not resolve a Transaction instance')
}
if (_transaction.kind !== TRANSACTION_KIND.AUTHORIZE) {
throw new Error(`Transaction kind must be '${TRANSACTION_KIND.AUTHORIZE}' to be voided`)
}
if (_transaction.status !== TRANSACTION_STATUS.SUCCESS) {
throw new Error(`Transaction status must be '${TRANSACTION_STATUS.SUCCESS}' to be voided`)
}
return this.app.services.PaymentGenericService.void(_transaction, paymentProcessor)
})
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('PaymentGenericService.void did not return Transaction instance')
}
return _transaction.save({transaction: options.transaction || null})
})
.then(_transaction => {
resTransaction = _transaction
const event = {
object_id: resTransaction.order_id,
object: 'order',
objects: [{
order: resTransaction.order_id
},{
transaction: resTransaction.id
}],
type: `order.transaction.void.${resTransaction.status}`,
message: `Order ID ${resTransaction.order_id} transaction ID ${ resTransaction.id } voided of ${ this.app.services.ProxyCartService.formatCurrency(resTransaction.amount,resTransaction.currency)} ${resTransaction.currency} ${resTransaction.status}`,
data: resTransaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return resTransaction
})
}
/**
*
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
refund(transaction, options){
options = options || {}
const Transaction = this.app.orm['Transaction']
const paymentProcessor = this.app.config.proxyGenerics[transaction.gateway]
|| this.app.config.get('proxyGenerics.payment_processor')
if (!paymentProcessor || !paymentProcessor.adapter) {
// TODO throw proper error
const err = new Error('Payment Processor is unspecified')
return Promise.reject(err)
}
transaction.description = transaction.description || 'Transaction Refund'
let resTransaction
return Transaction.resolve(transaction, {transaction: options.transaction || null })
.then(_transaction => {
if (!_transaction) {
throw new Errors.FoundError(Error('Transaction Not Found'))
}
if (!(_transaction instanceof Transaction)) {
throw new Error('Did not resolve a Transaction instance')
}
if ([TRANSACTION_KIND.CAPTURE, TRANSACTION_KIND.SALE].indexOf(_transaction.kind) === -1) {
throw new Error(`Transaction kind must be '${TRANSACTION_KIND.CAPTURE}' or '${TRANSACTION_KIND.SALE}' to be refunded`)
}
if (_transaction.status !== TRANSACTION_STATUS.SUCCESS) {
throw new Error(`Transaction status must be '${TRANSACTION_STATUS.SUCCESS}' to be refunded`)
}
return this.app.services.PaymentGenericService.refund(_transaction, paymentProcessor)
})
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('PaymentGenericService.refund did not return Transaction instance')
}
return _transaction.save({transaction: options.transaction || null})
})
.then(_transaction => {
resTransaction = _transaction
const event = {
object_id: resTransaction.order_id,
object: 'order',
objects: [{
order: resTransaction.order_id
},{
transaction: resTransaction.id
}],
type: `order.transaction.refund.${resTransaction.status}`,
message: `Order ID ${resTransaction.order_id} transaction ID ${ resTransaction.id } refund of ${ this.app.services.ProxyCartService.formatCurrency(resTransaction.amount,resTransaction.currency)} ${resTransaction.currency} ${resTransaction.status}`,
data: resTransaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return resTransaction
})
}
/**
*
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
retry(transaction, options) {
options = options || {}
const Transaction = this.app.orm['Transaction']
let resTransaction
return Transaction.resolve(transaction, {transaction: options.transaction || null })
.then(_transaction => {
if (!_transaction) {
throw new Errors.FoundError(Error('Transaction Not Found'))
}
if (!(_transaction instanceof Transaction)) {
throw new Error('Did not resolve a Transaction instance')
}
if ([TRANSACTION_STATUS.PENDING, TRANSACTION_STATUS.FAILURE].indexOf(_transaction.status) === -1) {
throw new Error('Transaction can not be tried if it is not pending or has not failed')
}
const paymentProcessor = this.app.config.proxyGenerics[_transaction.gateway]
|| this.app.config.get('proxyGenerics.payment_processor')
if (!paymentProcessor || !paymentProcessor.adapter) {
throw new Error('Payment Processor is unspecified')
}
_transaction.retry()
return this.app.services.PaymentGenericService[_transaction.kind](_transaction, paymentProcessor)
})
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('PaymentGenericService.retry did not return Transaction instance')
}
return _transaction.save({transaction: options.transaction || null})
})
.then(_transaction => {
resTransaction = _transaction
const event = {
object_id: resTransaction.order_id,
object: 'order',
objects: [{
order: resTransaction.order_id
},{
transaction: resTransaction.id
}],
type: `order.transaction.${resTransaction.kind}.${resTransaction.status}`,
message: `Order ID ${resTransaction.order_id} transaction ID ${ resTransaction.id } ${resTransaction.kind} of ${ this.app.services.ProxyCartService.formatCurrency(resTransaction.amount,resTransaction.currency)} ${resTransaction.currency} ${resTransaction.status}`,
data: resTransaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return resTransaction
})
}
/**
*
* @param transaction
* @param options
* @returns {Promise.<T>}
*/
cancel(transaction, options) {
options = options || {}
const Transaction = this.app.orm['Transaction']
let resTransaction
return Transaction.resolve(transaction, {transaction: options.transaction || null })
.then(_transaction => {
if (!_transaction) {
throw new Errors.FoundError(Error('Transaction Not Found'))
}
if (!(_transaction instanceof Transaction)) {
throw new Error('Did not resolve Transaction instance')
}
if ([TRANSACTION_STATUS.PENDING, TRANSACTION_STATUS.FAILURE].indexOf(_transaction.status) === -1) {
throw new Error('Transaction can not be cancelled if it is not pending or failed')
}
return _transaction.cancel().save({transaction: options.transaction || null})
})
.then(_transaction => {
if (!(_transaction instanceof Transaction)) {
throw new Error('Did not return Transaction instance')
}
resTransaction = _transaction
const event = {
object_id: resTransaction.order_id,
object: 'order',
objects: [{
order: resTransaction.order_id
},{
transaction: resTransaction.id
}],
type: 'order.transaction.cancelled',
message: `Order ID ${resTransaction.order_id} transaction ID ${ resTransaction.id } of ${ this.app.services.ProxyCartService.formatCurrency(resTransaction.amount,resTransaction.currency)} ${resTransaction.status}`,
data: resTransaction
}
return this.app.services.ProxyEngineService.publish(event.type, event, {
save: true,
transaction: options.transaction || null
})
})
.then(event => {
return resTransaction
})
}
}