使用 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 中获取错误的 code
和 message
的正确方法是调用错误对象本身的 .code and .message。
可能只是 error.Code
和 error.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));
突然之间,问题可能变得不言自明 - 并且很容易解决。
我正在开发一个网店,我的应用已经基本完成,目前正在开发支付部分。尽管出于安全原因,我的所有付款授权和配置都存储在 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 中获取错误的 code
和 message
的正确方法是调用错误对象本身的 .code and .message。
可能只是 error.Code
和 error.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));
突然之间,问题可能变得不言自明 - 并且很容易解决。