我的功能从移动应用程序接收数据。发出CASHIN请求时,函数postIntouch将Https请求发送给第三方API,然后将当前交易保存为Firecon集合中的未确认交易。同时等待带有事务状态的第三方回调。触发回调后,enter code here
函数通过检查用户集合以查找正确的交易并更新文档中的状态,然后将其发送到分类帐来更新用户余额。我的问题是,由于未确认的交易未保存在用户集合中,因此回调无法更新正确的用户余额。任何帮助,将不胜感激!
检查第三方发送的回调。 在集合中找不到交易的任何痕迹
/**
* Logic to initiate a cash in/out operation
*
* @param data
* {
* ...
* provider: 'ORANGE' | 'TELMOB' | 'TELECEL',
* operation: 'CASHIN' | 'CASHOUT' | 'AIRTIME',
* amount: number,
* otp: string,
* ...
* }
*
* @returns code 200 on transaction pending
* In case of errors, return message '5-Insufficiant funds' or '8-Amount cannot be negative'
*/
exports.postIntouch = functions.https.onCall(async (
data:
{
provider: _.IntouchProvider,
operation: _.IntouchOperation,
amount: number,
email : string
otp: string,
clientTimestamp: string
},
context) => {
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated', 'A user must be authenticated')
}
const { provider, operation, amount, otp,} = data
/* PRELIMINARY VALIDATION */
if (amount <= 0) throw new functions.https.HttpsError('invalid-argument', `${_.Errors.negativeAmount.code}-${_.Errors.negativeAmount.message}`)
if (_.INTOUCH_SERVICE[provider][operation].length === 0) throw new functions.https.HttpsError('unimplemented', 'Service not available')
/* FIND USER */
const user = await _.findUser({ uid: context.auth.uid })
if (user === undefined) throw new functions.https.HttpsError('not-found', 'Could not find user')
/* IF CASHOUT OR AIRTIME OR BILL, MAKE SURE USER HAS ENOUGH FUNDS */
if (operation === _.IntouchOperation.CASHOUT || operation === _.IntouchOperation.AIRTIME || operation === _.IntouchOperation.BILL) {
if (user.wallet.fcfa < amount) throw new functions.https.HttpsError('failed-precondition',
`${_.Errors.insufficiantFunds.code}-${_.Errors.insufficiantFunds.message}`
)
}
/* GENERATE TRANSACTION ID */
const transactionId = _.db.collection('users-beta').doc(user.uid).collection('transactions').doc().id
/* CALL INTOUCH API */
let resp
try {
resp = await _.initiateIntouchTransaction(amount, user.phoneNumber, `${user.uid}|${transactionId}`, provider, operation, otp)
} catch (error) {
console.log(error)
throw new functions.https.HttpsError('aborted', 'Intouch transaction failed')
}
//If transaction failed, stop execution right here
if (resp.status !== _.IntouchTransactionStatus.PENDING && resp.status !== _.IntouchTransactionStatus.INITIATED) {
throw new functions.https.HttpsError('aborted', 'Intouch transaction failed')
}
console.log('response '+resp)
/* UPDATE LEDGER AND FIRESTORE */
const batch = _.db.batch()
const userRef = _.db.collection('users-beta').doc(user.uid)
const transactionRef = _.db.collection('users-beta').doc(user.uid).collection('transactions').doc(transactionId)
console.log('user reference '+userRef)
console.log('transactionRef '+transactionRef)
const record: _.TransactionRecord = {
type: operation === _.IntouchOperation.CASHIN ? _.TransactionType.CASHIN
: operation === _.IntouchOperation.CASHOUT ? _.TransactionType.CASHOUT
: operation === _.IntouchOperation.AIRTIME ? _.TransactionType.AIRTIME
: _.TransactionType.BILL,
sender: {
uid: user.uid,
handle: '',
firstName: '',
lastName: otp
},
receiver: {
uid: '',
handle: '',
firstName: '',
lastName: ''
},
amount: amount,
passPhrase: '',
status: _.TransactionStatus.ONHOLD,
ledgerRecords: { first: {}, second: {} },
intouchResponses: { first: resp, second: {} },
clientTimestamp: data.clientTimestamp
}
//If CASHOUT OR AIRTIME OR BILL, reserve tokens on ledger
if (operation === _.IntouchOperation.CASHOUT || operation === _.IntouchOperation.AIRTIME || operation === _.IntouchOperation.BILL) {
//Ledger
let transaction
try {
transaction = await _.writeTransactionToLedger(_.TransactionType.RESERVATION, amount,
{ sourceAccountId: user.uid, destinationAccountId: operation })
} catch (error) {
console.log(error)
throw new functions.https.HttpsError('internal', 'Could not write transaction to ledger')
}
//Firestore
record.ledgerRecords.first = transaction
batch
.update(userRef, { wallet: { fcfa: user.wallet.fcfa - amount, points: user.wallet.points } })
.set(transactionRef, record)
} else if (operation === _.IntouchOperation.CASHIN) {
//Firestore
batch.set(transactionRef, record)
}
/* WRITE TO FIRESTORE */
try {
await batch.commit()
} catch (error) {
console.log(error)
throw new functions.https.HttpsError('unknown', 'Firestore write failed after intouch api call')
}
})
/**
* Callback for Intouch
*/
exports.intouchCallback = functions.https.onRequest(async (req, res) => {
const { partner_transaction_id, status } = req.body
if (typeof partner_transaction_id !== 'string' || typeof status !== 'string') { res.end(); return }
console.log(req.body)
//Extract both from partnerTransactionId
const [uid, transactionId] = partner_transaction_id.split('|')
/* LOOKUP USER AND TRANSACTION */
const user = await _.findUser({ uid: uid })
if (user === undefined) { res.end(); return }
const transaction = await _.findTransaction(transactionId, { senderUid: uid })
if (transaction === undefined) {
console.log('cannot find transaction')
res.end();
return
}
/* UPDATE BALANCE ON LEDGER AND FIRESTORE BASED ON STATUS */
const batch = _.db.batch()
const userRef = _.db.collection('users-beta').doc(uid)
const transactionRef = _.db.collection('users-beta').doc(uid).collection('transactions').doc(transactionId)
let ledgerRecord
if (status === _.IntouchTransactionStatus.SUCCESSFUL) {
if (transaction.type === _.TransactionType.CASHIN) {
//WRITE TO LEDGER
try {
ledgerRecord = await _.writeTransactionToLedger(transaction.type, transaction.amount, { destinationAccountId: uid })
} catch (error) {
console.log(error)
res.end();
return error
}
//Update user balance
batch.update(userRef, { wallet: { fcfa: user.wallet.fcfa + transaction.amount, points: user.wallet.points } })
//Update transaction record
batch.update(transactionRef,
{
status: _.IntouchTransactionStatus.SUCCESSFUL,
ledgerRecords: { first: ledgerRecord, second: {} },
intouchResponses: { first: transaction.intouchResponses.first, second: req.body }
}
)
} else if (transaction.type === _.TransactionType.CASHOUT || transaction.type === _.TransactionType.AIRTIME || transaction.type === _.TransactionType.BILL) {
//WRITE TO LEDGER
try {
ledgerRecord = await _.writeTransactionToLedger(transaction.type, transaction.amount, { sourceAccountId: uid })
} catch (error) {
console.log(error)
res.end(); return
}
//Update transaction record
batch.update(transactionRef,
{
status: _.IntouchTransactionStatus.SUCCESSFUL,
ledgerRecords: { first: transaction.ledgerRecords.first, second: ledgerRecord },
intouchResponses: { first: transaction.intouchResponses.first, second: req.body }
}
)
}
} else if (status === _.IntouchTransactionStatus.FAILED) {
//Update status and save intouch response in transaction record
batch.update(transactionRef,
{
status: _.IntouchTransactionStatus.FAILED,
intouchResponses: { first: transaction.intouchResponses.first, second: req.body }
}
)
if (transaction.type === _.TransactionType.CASHOUT || transaction.type === _.TransactionType.AIRTIME || transaction.type === _.TransactionType.BILL) {
//CANCEL TRANSACTION ON LEDGER
const operation = transaction.type === _.TransactionType.CASHOUT ? _.IntouchOperation.CASHOUT
: transaction.type === _.TransactionType.AIRTIME ? _.IntouchOperation.AIRTIME
: _.IntouchOperation.BILL
try {
ledgerRecord = await _.writeTransactionToLedger(_.TransactionType.CANCELLATION, transaction.amount,
{ sourceAccountId: uid, destinationAccountId: operation })
} catch (error) {
console.log(error)
res.end(); return
}
//REVERT BALANCE IN FIRESTORE
batch
.update(userRef, { wallet: { fcfa: user.wallet.fcfa + transaction.amount, points: user.wallet.points } })
.update(transactionRef, { ledgerRecords: { first: transaction.ledgerRecords.first, second: ledgerRecord } })
}
} else { res.end(); return }
//Commit batch
try {
await batch.commit()
} catch (error) {
console.log(error)
}
/* SEND NOTIFICATION TO USER */
if (status === _.IntouchTransactionStatus.SUCCESSFUL) {
try {
switch (transaction.type) {
case _.TransactionType.CASHIN: {
await _.notify(user.notificationToken, `Votre balance a été creditée de ${transaction.amount}`, '', { transactionId: transactionId })
break
}
case _.TransactionType.CASHOUT: {
await _.notify(user.notificationToken, `Votre balance a été debitée de ${transaction.amount}`, '', { transactionId: transactionId })
break
}
case _.TransactionType.AIRTIME: {
await _.notify(user.notificationToken, `Votre achat de ${transaction.amount} d'unités a réussi`, '', { transactionId: transactionId })
break
}
/* case _.TransactionType.BILL: {
} */
default: {
break
}
}
} catch (error) {
console.log(error)
}
} else if (status === _.IntouchTransactionStatus.FAILED) {
try {
await _.notify(user.notificationToken, `Votre transaction ${transactionId} a echouée`, '', { transactionId: transactionId })
} catch (error) {
console.log(error)
}
}
res.end(); return
})
答案 0 :(得分:0)
我无法彻底检查代码,但是您期望
const transactionId = _.db.collection('users-beta').doc(user.uid).collection('transactions').doc().id
将数据添加到Firestore。如果是这样,则不会。
您需要执行.add或.set来执行此操作。