【问题标题】:Can't return transaction response from callback to user无法将回调的交易响应返回给用户
【发布时间】:2020-02-28 03:31:57
【问题描述】:

我们正在使用 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 都同步运行它们,因此您可以轻松地将响应发送回用户。

所以,我认为必须有一种方法可以返回响应,我只是不知道如何。谢谢。

【问题讨论】:

  • 您必须将回调“承诺”为一个新的承诺,您可以将其返回给使用您要发送的数据进行解析的客户端。如果 API 没有返回承诺,Await 将不会做任何事情。

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


【解决方案1】:

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

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

Node/Firebase onCall asynchronous function return

【讨论】:

    【解决方案2】:

    使用下面的代码。它为我工作。

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

    【讨论】:

    • 代码转储不能提供好的答案。你应该解释如何为什么这可以解决他们的问题。我推荐阅读,“How do I write a good answer?"
    【解决方案3】:

    Pritesh Mahajan 的答案在正确的轨道上,但我不知道它对他有什么作用,因为抛出和返回会破坏代码。这是一个更完整的示例,将 ctrl.execute 转换为与 await 一起使用的 Promise。

    此示例还消除了对全局变量的需求,该变量永远不应该用于 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;
    }
    

    【讨论】:

      猜你喜欢
      • 2019-09-23
      • 1970-01-01
      • 1970-01-01
      • 2013-11-27
      • 2023-04-03
      • 2016-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多