无法从回调向用户返回事务响应

时间:2019-11-01 21:10:39

标签: node.js typescript firebase google-cloud-functions authorize.net

我们正在使用authorize.net Node SDK来处理付款。我们有一个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都是同步运行的,因此您可以轻松地将响应发送回用户。

所以,我认为必须有一种返回响应的方法,我只是不知道该怎么做。谢谢。

3 个答案:

答案 0 :(得分:1)

问题在于所提供的SDK无法返回承诺,因此无法等待任何返回。我们的解决方案是放弃Authorize.net的SDK,并从头开始构建。幸运的是,我们不必考虑API的每个端点,而只需要考虑我们需要的部分。我发现这个问题对于firebase可调用函数非常有用。

我们还提出了issue on the github存储库,因此希望我们能够继续进行SDK的更改。

Node/Firebase onCall asynchronous function return

答案 1 :(得分:0)

使用以下代码。它为我工作。

// 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)

          }
});
    }); 

答案 2 :(得分:0)

Pritesh Mahajan 的回答是正确的,但我不知道这对他来说如何,因为抛出和返回会破坏代码。这是一个更完整的示例,其中 ctrl.execute 转换为与 await 一起使用的承诺。

这个例子也不需要一个全局变量,它永远不应该用于 Node 中的事务值。

我还使用了 togo 包中的 to() 函数,该函数实现了 go 语言中的 to 功能,从而无需大型 try / catch 块。

这个例子是一个完整的节点项目示例,它使用运行 NodeJS 14 或更高版本的 ES6 模块。

这是为与 Accept.js 一起使用而设置的,使用 Authorize.Net 托管支付信息表单以符合 PCI-DSS SAQ A 的方式收集卡信息。

有关详细信息,请参阅 https://developer.authorize.net/api/reference/features/acceptjs.html#Using_the_Hosted_Payment_Information_Form

成功交易所需的唯一数据是 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;
}