使用 Cielo Payment 和 Google 云功能进行付款授权时出现问题

Problem with payment authorizing with Cielo Payment and Google Cloud functions

我正在开发一个网店,我的应用已经基本完成,目前正在开发支付部分。尽管出于安全原因,我的所有付款授权和配置都存储在 google 云功能中,但我的应用程序仍然存在。对于付款,我使用的是 Cielo Payment 沙箱,这是一种具有沙箱的付款方式,因此我可以进行测试。我在 google 云中的函数中遇到了一些错误,这是不应该发生的。

这是 cielo 的网站,因此我可以获得沙箱的密钥:https://cadastrosandbox.cieloecommerce.cielo.com.br/
我得到的错误:

Unhandled error TypeError: Cannot read property 'Code' of undefined

这是我的 index.ts:

import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import {CieloConstructor, Cielo,
  TransactionCreditCardRequestModel,
  EnumBrands, CaptureRequestModel} from "cielo";
// CaptureRequestModel, CancelTransactionRequestModel,
// TransactionCreditCardResponseModel

admin.initializeApp(functions.config().firebase);

// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript

const merchantId = functions.config().cielo.merchantid;
const merchantKey = functions.config().cielo.merchantkey;

const cieloParams: CieloConstructor = {
  merchantId: merchantId,
  merchantKey: merchantKey,
  sandbox: true,
  debug: true, // Remover quando for fazer transações reais
};

const cielo = new Cielo(cieloParams);

export const authorizeCreditCard = functions.https.
    onCall( async (data, context) => {
      if (data === null) {
        return {"success": false, "error":
        {"code": -1, "message": "Dados não informados"}};
      }

      if (!context.auth) {
        return {"success": false, "error":
        {"code": -1, "message": "Nenhum usuário logado"}};
      }

      const userId = context.auth.uid;

      const snapshot = await admin.firestore().collection("users").
          doc(userId).get();
      const userData = snapshot.data() || {};

      console.log("Iniciando autorização");

      let brand: EnumBrands;
      switch (data.creditCard.brand) {
        case "VISA":
          brand = EnumBrands.VISA;
          break;
        case "MASTERCARD":
          brand = EnumBrands.MASTER;
          break;
        case "AMEX":
          brand = EnumBrands.AMEX;
          break;
        case "ELO":
          brand = EnumBrands.ELO;
          break;
        case "JCB":
          brand = EnumBrands.JCB;
          break;
        case "DINERSCLUB":
          brand = EnumBrands.DINERS;
          break;
        case "DISCOVER":
          brand = EnumBrands.DISCOVERY;
          break;
        case "HIPERCARD":
          brand = EnumBrands.HIPERCARD;
          break;
        default:
          return {
            "success": false, "error": {
              "code": -1, "message": "Cartão não suportado: " +
              data.creditCard.brand,
            },
          };
      }

      const saleData: TransactionCreditCardRequestModel = {
        merchantOrderId: data.merchantOrderId,
        customer: {
          name: userData.name,
          identity: data.cpf,
          identityType: "CPF",
          email: userData.email,
          deliveryAddress: {
            street: userData.address.street,
            number: userData.address.number,
            complement: userData.address.complement,
            zipCode: userData.address.zipCode.replace(".", "").replace("-", ""),
            city: userData.address.city,
            state: userData.address.state,
            country: "BRA",
            district: userData.address.district,
          },
        },
        payment: {
          currency: "BRL",
          country: "BRA",
          amount: data.amount,
          installments: data.installments,
          softDescriptor: data.softDescriptor.substring(0, 13),
          type: data.paymentType,
          capture: false,
          creditCard: {
            cardNumber: data.creditCard.cardNumber,
            holder: data.creditCard.holder,
            expirationDate: data.creditCard.expirationDate,
            securityCode: data.creditCard.securityCode,
            brand: brand,
          },
        },
      };

      try {
        const transaction = await cielo.creditCard.transaction(saleData);

        if (transaction.payment.status === 1) {
          return {
            "success": true,
            "paymentId": transaction.payment.paymentId,
          };
        } else {
          let message = "";
          switch (transaction.payment.returnCode) {
            case "5":
              message = "Não Autorizada";
              break;
            case "57":
              message = "Cartão expirado";
              break;
            case "78":
              message = "Cartão bloqueado";
              break;
            case "99":
              message = "Timeout";
              break;
            case "77":
              message = "Cartão cancelado";
              break;
            case "70":
              message = "Problemas com o Cartão de Crédito";
              break;
            default:
              message = transaction.payment.returnMessage;
              break;
          }
          return {
            "success": false,
            "status": transaction.payment.status,
            "error": {
              "code": transaction.payment.returnCode,
              "message": message,
            },
          };
        }
      } catch (error) {
        return {
          "success": false,
          "error": {
            "code": error.response[0].Code,
            "message": error.response[0].Message,
          },
        };
      }
    });

export const captureCreditCard = functions.https.
    onCall( async (data, context) => {
      if (data === null) {
        return {"success": false, "error":
        {"code": -1, "message": "Dados não informados"}};
      }

      if (!context.auth) {
        return {"success": false, "error":
        {"code": -1, "message": "Nenhum usuário logado"}};
      }

      const captureParams: CaptureRequestModel = {
        paymentId: data.payId,
      };

      try {
        const capture = await cielo.creditCard.
            captureSaleTransaction(captureParams);

        if (capture.status == 2) {
          return {
            "success": true,
          };
        } else {
          return {
            "success": false,
            "status": capture.status,
            "error": {
              "code": capture.returnCode,
              "message": capture.returnMessage,
            },
          };
        }
      } catch (error) {
        return {
          "success": false,
          "error": {
            "code": error.response[0].Code,
            "message": error.response[0].Message,
          },
        };
      }
    });

我的cielo_payment.dart:

import 'dart:collection';

import 'package:cloud_functions/cloud_functions.dart';
import 'package:flutter/cupertino.dart';
import 'package:loja_virtual_nnananene/models/credit_card.dart';
import 'package:loja_virtual_nnananene/models/user.dart';

class CieloPayment {
  final functions = CloudFunctions.instance;

  Future<String>? authorize(
      {CreditCard? creditCard, num? price, String? orderId, User? user}) async {
    try {
      final Map<String, dynamic> dataSale = {
        'merchantOrderId': orderId,
        'amount': (price! * 100).toInt(),
        'softDescriptor': 'Nnananene',
        'installments': 1,
        'creditCard': creditCard!.toJson(),
        'cpf': user!.cpf,
        'paymentType': 'CreditCard',
      };

      final HttpsCallable callable =
          functions.getHttpsCallable(functionName: 'authorizeCreditCard');

      callable.timeout = const Duration(seconds: 60);

      final response = await callable.call(dataSale);
      print(response.data);
      final data = Map<String, dynamic>.from(response.data);

      if (data['success'] as bool) {
        return data['paymentId'];
      } else {
        debugPrint('${data['error']['message']}');
        return Future.error(data['error']['message']);
      }
    } catch (e) {
      debugPrint('$e');
      return Future.error('Falha ao processar transação. Tente novamente.');
    }
  }

  Future<void> capture(String payId) async {
    final Map<String, dynamic> captureData = {'payId': payId};
    final HttpsCallable callable =
        functions.getHttpsCallable(functionName: 'captureCreditCard');
    callable.timeout = const Duration(seconds: 60);
    final response = await callable.call(captureData);
    final data = Map<String, dynamic>.from(response.data as LinkedHashMap);

    if (data['success'] as bool) {
      debugPrint('Captura realizada com sucesso');
    } else {
      debugPrint('${data['error']['message']}');
      return Future.error(data['error']['message']);
    }
  }

  Future<void> cancel(String payId) async {
    final Map<String, dynamic> cancelData = {'payId': payId};
    final HttpsCallable callable =
        functions.getHttpsCallable(functionName: 'cancelCreditCard');
    callable.timeout = const Duration(seconds: 60);
    final response = await callable.call(cancelData);
    final data = Map<String, dynamic>.from(response.data as LinkedHashMap);

    if (data['success'] as bool) {
      debugPrint('Cancelamento realizado com sucesso');
    } else {
      debugPrint('${data['error']['message']}');
      return Future.error(data['error']['message']);
    }
  }
}

我的 checkout_manager.dart 发生一切的地方:

Future<void> checkout(
      {CreditCard? creditCard,
      Function? onStockFail,
      Function? onSuccess,
      Function? onPayFail}) async {
    loading = true;

    final orderId = await _getOrderId();

    try {
      String? payId = await cieloPayment.authorize(
        creditCard: creditCard,
        price: cartManager!.totalPrice,
        orderId: orderId.toString(),
        user: cartManager!.user,
      );
      debugPrint('success $payId');
    } catch (e) {
      onPayFail!(e);
      loading = false;
      return;
    }

    try {
      await _decrementStock();
    } catch (e) {
      onStockFail!(e);
      loading = false;
      return;
    }

这是错误所指的行:

line 150:43 -  "code": error.respose[0].Code, // 43 refers to the dot in .Code
line 95:5 -  cardNumber: data.creditCard.cardNumber,


这是我从我的应用程序中得到的错误,这不是真正的信用卡,并且在沙盒中,当卡以 1 结束时,交易成功。 这是来自 dataSale 和响应的打印:

修复后:

错误是在卡到期日期验证时,有空格导致错误

更改以下内容

 error.response[0].Code
 error.response[0].Message

对此:

error.code
error.message

在 Node.js 中获取错误的 codemessage 的正确方法是调用错误对象本身的 .code and .message

可能只是 error.Codeerror.Message ...根据 API 的 documentation. There's even a listing of all possible error codes 可用 - 虽然 Flutter 无关紧要,但 Cloud功能无法正常运行(在那之前,Flutter 只会增加不相关的复杂性)。

最好使用 Cloud Logging 登录 Cloud Function... 稍后再考虑 Flutter。这比尝试猜测错误可能是什么然后询问 SO 更有效。

例如,要确定返回的错误是什么,这将是:

functions.logger.error(JSON.stringify(error));

突然之间,问题可能变得不言自明 - 并且很容易解决。