无法 return 从回调到用户的事务响应
Can't return transaction response from callback to user
我们正在使用 authorize.net Node SDK to process payments. We have a Firebase callable function 来处理处理付款的请求,但我们似乎无法获得交易的响应。
问题出在下面的代码中。
try {
// MAKE GLOBAL VARIABLE TO HOLD RESPONSE? -> (problems with async callback)
let RESPONSE_FOR_CLIENT;
await ctrl.execute(async function () {
var apiResponse = ctrl.getResponse();
var response = await new ApiContracts.CreateTransactionResponse(apiResponse);
RESPONSE_FOR_CLIENT = response;
if (response != null) {
if (response.getMessages().getResultCode() == ApiContracts.MessageTypeEnum.OK) {
if (response.getTransactionResponse().getMessages() != null) {
// ... do stuff
}
else {
console.log('Failed Transaction.');
if (response.getTransactionResponse().getErrors() != null) {
// ... do stuff
}
}
}
else {
console.log('Failed Transaction. ');
}
});
return RESPONSE_FOR_CLIENT;
} catch (error) {
throw new functions.https.HttpsError('unknown', error);
}
是的,我知道问题在于 ctrl.execute 是一个回调函数,我真的很困惑为什么 authorize.net 以这种方式实现它。 java 和 python SDK 全部 运行 它们是同步的,因此您可以轻松地将响应发送回用户。
所以,我认为一定有办法 return 响应,我只是不知道如何。谢谢
问题是所提供的 SDK 没有 return 承诺,因此无法等待任何 return。我们的解决方案是放弃 Authorize.net 的 SDK 并从头开始构建。幸运的是,我们不必考虑它们 API 的每个端点,只需考虑我们需要的部分。我发现这个问题对于 firebase 可调用函数部分非常有启发性。
我们还提出了一个 issue on the github 存储库,因此希望我们能够继续对 SDK 进行更改。
使用下面的代码。它为我工作。
// MAKE GLOBAL VARIABLE TO HOLD RESPONSE? -> (problems with async callback)
let RESPONSE_FOR_CLIENT;
return new Promise(function (resolve, reject) {
ctrl.execute(function () {
var apiResponse = ctrl.getResponse();
var response = new ApiContracts.CreateTransactionResponse(apiResponse);
RESPONSE_FOR_CLIENT = response;
if (response != null) {
if (response.getMessages().getResultCode() == ApiContracts.MessageTypeEnum.OK) {
if (response.getTransactionResponse().getMessages() != null) {
return resolve(response)
}
else {
console.log('Failed Transaction.');
if (response.getTransactionResponse().getErrors() != null) {
throw reject(response.getTransactionResponse().getErrors())
}
}
}
else {
throw reject(''Error Message)
}
});
});
Pritesh Mahajan 的回答是正确的,但我不知道他是如何工作的,因为 throws 和 returns 破坏了代码。这是一个更完整的示例,其中 ctrl.execute 转换为与 await 一起使用的承诺。
此示例还消除了对全局变量的需求,该变量永远不应用于 Node 中的事务值。
我还使用了 togo 包中的 to() 函数,它实现了 go 语言中的 to 功能,消除了对大型 try / catch 块的需要。
此示例是使用 ES6 模块的节点项目的完整示例运行 NodeJS 14 或更高版本。
此设置与 Accept.js 一起使用,使用 Authorize.Net 托管支付信息表以符合 PCI-DSS SAQ A 的方式收集卡信息。
成功交易所需的唯一数据是 authData 和 paymentData。以运输数据为例,如果您需要 Authorize.NET API 包含的任何其他数据,可以很容易地修改此功能以支持它。
import authorizenet from "authorizenet";
const ApiContracts = authorizenet.APIContracts;
const ApiControllers = authorizenet.APIControllers;
const SDKConstants = authorizenet.Constants;
function to (promise)
{
return promise
.then(val => [null, val])
.catch(err => [err]);
}
async function chargeCustomer (params = {}) {
/*
params = {
authData: {
api_login_id,
transaction_key,
endpoint
}
paymentData: {
opaqueData: {
dataDescriptor,
dataValue
},
amount
}
shipTo: {
firstName,
lastName,
company,
address,
city,
state,
zip,
country
}
}
*/
if ((!params?.authData?.api_login_id) || (!params?.authData?.transaction_key)) {
throw "missing credentials";
}
let merchantAuthenticationType = new ApiContracts.MerchantAuthenticationType();
merchantAuthenticationType.setName(params.authData.api_login_id);
merchantAuthenticationType.setTransactionKey(params.authData.transaction_key);
let opaqueData = new ApiContracts.OpaqueDataType();
opaqueData.setDataDescriptor(params.opaqueData.dataDescriptor);
opaqueData.setDataValue(params.opaqueData.dataValue);
let paymentType = new ApiContracts.PaymentType();
paymentType.setOpaqueData(opaqueData);
let shipTo = new ApiContracts.CustomerAddressType();
shipTo.setFirstName(params?.shipTo?.firstName || null);
shipTo.setLastName(params?.shipTo?.last_name || null);
shipTo.setCompany(params?.shipTo?.company || null);
shipTo.setAddress(params?.shipTo?.address || null);
shipTo.setCity(params?.shipTo?.city || null);
shipTo.setState(params?.shipTo?.state || null);
shipTo.setZip(params?.shipTo?.zip || null);
shipTo.setCountry(params?.shipTo?.country || null);
let transactionRequestType = new ApiContracts.TransactionRequestType();
transactionRequestType.setTransactionType(ApiContracts.TransactionTypeEnum.AUTHCAPTURETRANSACTION);
transactionRequestType.setPayment(paymentType);
transactionRequestType.setAmount(params?.paymentData?.amount);
transactionRequestType.setShipTo(shipTo);
let createRequest = new ApiContracts.CreateTransactionRequest();
createRequest.setMerchantAuthentication(merchantAuthenticationType);
createRequest.setTransactionRequest(transactionRequestType);
let ctrl = new ApiControllers.CreateTransactionController(createRequest.getJSON());
if (params?.authData?.endpoint === "production") {
ctrl.setEnvironment(SDKConstants.endpoint.production);
}
let [err, response] = await to(new Promise((resolve, reject) => {
ctrl.execute( () => {
let apiResponse = ctrl.getResponse();
let response = new ApiContracts.CreateTransactionResponse(apiResponse);
if (response != null) {
if (response.getMessages().getResultCode() == ApiContracts.MessageTypeEnum.OK) {
if (response.getTransactionResponse().getMessages() != null) {
console.log(JSON.stringify(response));
resolve(response);
} else {
console.debug('Failed Transaction.');
if (response.getTransactionResponse().getErrors() != null) {
reject(response.getTransactionResponse().getErrors())
}
}
} else {
reject('null response from Authorize.Net');
}
};
});
}));
if (err) {
throw err;
}
return response;
}
我们正在使用 authorize.net Node SDK to process payments. We have a Firebase callable function 来处理处理付款的请求,但我们似乎无法获得交易的响应。
问题出在下面的代码中。
try {
// MAKE GLOBAL VARIABLE TO HOLD RESPONSE? -> (problems with async callback)
let RESPONSE_FOR_CLIENT;
await ctrl.execute(async function () {
var apiResponse = ctrl.getResponse();
var response = await new ApiContracts.CreateTransactionResponse(apiResponse);
RESPONSE_FOR_CLIENT = response;
if (response != null) {
if (response.getMessages().getResultCode() == ApiContracts.MessageTypeEnum.OK) {
if (response.getTransactionResponse().getMessages() != null) {
// ... do stuff
}
else {
console.log('Failed Transaction.');
if (response.getTransactionResponse().getErrors() != null) {
// ... do stuff
}
}
}
else {
console.log('Failed Transaction. ');
}
});
return RESPONSE_FOR_CLIENT;
} catch (error) {
throw new functions.https.HttpsError('unknown', error);
}
是的,我知道问题在于 ctrl.execute 是一个回调函数,我真的很困惑为什么 authorize.net 以这种方式实现它。 java 和 python SDK 全部 运行 它们是同步的,因此您可以轻松地将响应发送回用户。
所以,我认为一定有办法 return 响应,我只是不知道如何。谢谢
问题是所提供的 SDK 没有 return 承诺,因此无法等待任何 return。我们的解决方案是放弃 Authorize.net 的 SDK 并从头开始构建。幸运的是,我们不必考虑它们 API 的每个端点,只需考虑我们需要的部分。我发现这个问题对于 firebase 可调用函数部分非常有启发性。
我们还提出了一个 issue on the github 存储库,因此希望我们能够继续对 SDK 进行更改。
使用下面的代码。它为我工作。
// MAKE GLOBAL VARIABLE TO HOLD RESPONSE? -> (problems with async callback)
let RESPONSE_FOR_CLIENT;
return new Promise(function (resolve, reject) {
ctrl.execute(function () {
var apiResponse = ctrl.getResponse();
var response = new ApiContracts.CreateTransactionResponse(apiResponse);
RESPONSE_FOR_CLIENT = response;
if (response != null) {
if (response.getMessages().getResultCode() == ApiContracts.MessageTypeEnum.OK) {
if (response.getTransactionResponse().getMessages() != null) {
return resolve(response)
}
else {
console.log('Failed Transaction.');
if (response.getTransactionResponse().getErrors() != null) {
throw reject(response.getTransactionResponse().getErrors())
}
}
}
else {
throw reject(''Error Message)
}
});
});
Pritesh Mahajan 的回答是正确的,但我不知道他是如何工作的,因为 throws 和 returns 破坏了代码。这是一个更完整的示例,其中 ctrl.execute 转换为与 await 一起使用的承诺。
此示例还消除了对全局变量的需求,该变量永远不应用于 Node 中的事务值。
我还使用了 togo 包中的 to() 函数,它实现了 go 语言中的 to 功能,消除了对大型 try / catch 块的需要。
此示例是使用 ES6 模块的节点项目的完整示例运行 NodeJS 14 或更高版本。
此设置与 Accept.js 一起使用,使用 Authorize.Net 托管支付信息表以符合 PCI-DSS SAQ A 的方式收集卡信息。
成功交易所需的唯一数据是 authData 和 paymentData。以运输数据为例,如果您需要 Authorize.NET API 包含的任何其他数据,可以很容易地修改此功能以支持它。
import authorizenet from "authorizenet";
const ApiContracts = authorizenet.APIContracts;
const ApiControllers = authorizenet.APIControllers;
const SDKConstants = authorizenet.Constants;
function to (promise)
{
return promise
.then(val => [null, val])
.catch(err => [err]);
}
async function chargeCustomer (params = {}) {
/*
params = {
authData: {
api_login_id,
transaction_key,
endpoint
}
paymentData: {
opaqueData: {
dataDescriptor,
dataValue
},
amount
}
shipTo: {
firstName,
lastName,
company,
address,
city,
state,
zip,
country
}
}
*/
if ((!params?.authData?.api_login_id) || (!params?.authData?.transaction_key)) {
throw "missing credentials";
}
let merchantAuthenticationType = new ApiContracts.MerchantAuthenticationType();
merchantAuthenticationType.setName(params.authData.api_login_id);
merchantAuthenticationType.setTransactionKey(params.authData.transaction_key);
let opaqueData = new ApiContracts.OpaqueDataType();
opaqueData.setDataDescriptor(params.opaqueData.dataDescriptor);
opaqueData.setDataValue(params.opaqueData.dataValue);
let paymentType = new ApiContracts.PaymentType();
paymentType.setOpaqueData(opaqueData);
let shipTo = new ApiContracts.CustomerAddressType();
shipTo.setFirstName(params?.shipTo?.firstName || null);
shipTo.setLastName(params?.shipTo?.last_name || null);
shipTo.setCompany(params?.shipTo?.company || null);
shipTo.setAddress(params?.shipTo?.address || null);
shipTo.setCity(params?.shipTo?.city || null);
shipTo.setState(params?.shipTo?.state || null);
shipTo.setZip(params?.shipTo?.zip || null);
shipTo.setCountry(params?.shipTo?.country || null);
let transactionRequestType = new ApiContracts.TransactionRequestType();
transactionRequestType.setTransactionType(ApiContracts.TransactionTypeEnum.AUTHCAPTURETRANSACTION);
transactionRequestType.setPayment(paymentType);
transactionRequestType.setAmount(params?.paymentData?.amount);
transactionRequestType.setShipTo(shipTo);
let createRequest = new ApiContracts.CreateTransactionRequest();
createRequest.setMerchantAuthentication(merchantAuthenticationType);
createRequest.setTransactionRequest(transactionRequestType);
let ctrl = new ApiControllers.CreateTransactionController(createRequest.getJSON());
if (params?.authData?.endpoint === "production") {
ctrl.setEnvironment(SDKConstants.endpoint.production);
}
let [err, response] = await to(new Promise((resolve, reject) => {
ctrl.execute( () => {
let apiResponse = ctrl.getResponse();
let response = new ApiContracts.CreateTransactionResponse(apiResponse);
if (response != null) {
if (response.getMessages().getResultCode() == ApiContracts.MessageTypeEnum.OK) {
if (response.getTransactionResponse().getMessages() != null) {
console.log(JSON.stringify(response));
resolve(response);
} else {
console.debug('Failed Transaction.');
if (response.getTransactionResponse().getErrors() != null) {
reject(response.getTransactionResponse().getErrors())
}
}
} else {
reject('null response from Authorize.Net');
}
};
});
}));
if (err) {
throw err;
}
return response;
}