我正在致力于将无现金支付集成到我的 Flask Web 应用程序中。在此过程中,我遇到了一个问题,即负载中的某些字段包含
None
值,这似乎导致 API 请求出现问题。
这是我的 Flask 后端代码的相关部分:
import uuid
import json
from flask import Flask, request, jsonify
import requests
import os
from dotenv import load_dotenv
from flask_cors import CORS
from urllib.parse import quote
import logging
load_dotenv()
logging.basicConfig(level=logging.DEBUG)
app = Flask(__name__)
CORS(app)
CASHFREE_APP_ID = os.getenv('CASHFREE_APP_ID')
CASHFREE_SECRET_KEY = os.getenv('CASHFREE_SECRET_KEY')
CASHFREE_API_URL = "https://sandbox.cashfree.com/pg/orders"
@app.route('/create_order', methods=['POST'])
def create_order():
try:
data = request.json
logging.debug(f"Request data: {data}")
order_id = data.get('order_id', str(uuid.uuid4()))
order_amount = data.get('order_amount')
order_currency = 'INR'
customer_id = data.get('customer_id', 'default_customer_id')
customer_name = data.get('customer_name', 'teerkhelo')
customer_email = data.get('customer_email', '[email protected]')
customer_phone = data.get('customer_phone', '9612388891')
return_url = (
f'http://localhost:14128/payment_response?order_id={quote(str(order_id))}'
f'&status=success'
f'&user_name={quote(str(customer_name))}'
f'&user_mobile_number={quote(str(customer_phone))}'
f'&total_amount={quote(str(data.get("total_amount", "")))}'
f'&selected_numbers={quote(json.dumps(data.get("selected_numbers", [])))}'
f'&selected_ranges={quote(json.dumps(data.get("selected_ranges", [])))}'
f'&hou_heda_values={quote(json.dumps(data.get("hou_heda_values", {})))}'
f'&hou_jeda_values={quote(json.dumps(data.get("hou_jeda_values", {})))}'
f'&f_r_amounts={quote(json.dumps([0 if v is None else v for v in data.get("f_r_amounts", [])]))}'
f'&s_r_amounts={quote(json.dumps([0 if v is None else v for v in data.get("s_r_amounts", [])]))}'
f'&f_hou_amount={quote(str(data.get("f_hou_amount", "")))}'
f'&s_hou_amount={quote(str(data.get("s_hou_amount", "")))}'
)
headers = {
'Content-Type': 'application/json',
'x-client-id': CASHFREE_APP_ID,
'x-client-secret': CASHFREE_SECRET_KEY,
'x-api-version': '2023-08-01'
}
payload = {
'order_id': order_id,
'order_amount': order_amount,
'order_currency': order_currency,
'customer_details': {
'customer_id': customer_id,
'customer_name': customer_name,
'customer_email': customer_email,
'customer_phone': customer_phone
},
'order_meta': {
'return_url': return_url
}
}
logging.debug(f"Payload: {payload}")
response = requests.post(CASHFREE_API_URL, json=payload, headers=headers)
response_data = response.json()
logging.debug(f"Response: {response_data}")
if response.status_code == 200:
payment_session_id = response_data.get('payment_session_id', '')
if payment_session_id:
return jsonify({
'order_id': order_id,
'payment_session_id': payment_session_id
})
else:
return jsonify({'error': 'Payment session ID not found'}), 500
else:
return jsonify({'error': response_data.get('message', 'Unknown error occurred')}), response.status_code
except Exception as e:
logging.error(f"Exception occurred: {e}")
return jsonify({'error': 'An error occurred', 'details': str(e)}), 500
@app.route('/initiate_payment', methods=['POST'])
def initiate_payment():
data = request.json
order_id = data.get('order_id')
order_amount = data.get('order_amount')
payment_url = "https://sandbox.cashfree.com/checkout"
payload = {
'order_id': order_id,
'order_amount': order_amount,
'order_currency': 'INR',
'return_url': f'http://localhost:14128/payment_response?order_id={order_id}'
}
response = requests.post(payment_url, json=payload, headers={
'Content-Type': 'application/json',
'x-client-id': CASHFREE_APP_ID,
'x-client-secret': CASHFREE_SECRET_KEY,
})
if response.status_code == 200:
data = response.json()
return jsonify({'payment_url': data.get('payment_url')})
else:
return jsonify({'error': 'Failed to initiate payment'}), response.status_code
@app.route('/payment_response', methods=['GET'])
def payment_response():
data = request.args.to_dict()
order_id = data.get('order_id')
user_name = data.get('customer_name')
user_mobile_number = data.get('customer_phone')
# Verify the payment with Cashfree
payment_verification_url = f'https://sandbox.cashfree.com/pg/orders/{order_id}'
headers = {
'x-client-id': CASHFREE_APP_ID,
'x-client-secret': CASHFREE_SECRET_KEY,
}
verification_response = requests.get(payment_verification_url, headers=headers)
verification_data = verification_response.json()
if verification_response.status_code == 200 and verification_data.get('order_status') == 'PAID':
# Payment is verified
return jsonify({
'message': 'Payment verified',
'order_id': order_id,
'status': 'success',
'redirect_url': 'image_screen',
'user_name': user_name, # Include userName
'user_mobile_number': user_mobile_number # Include userMobileNumber
})
else:
# Payment not verified
return jsonify({
'message': 'Payment verification failed',
'order_id': order_id,
'status': 'failed'
})
@app.route('/payment_notification', methods=['POST'])
def payment_notification():
data = request.form.to_dict()
# Handle payment notification, usually updating the order status based on the notification.
return jsonify({'message': 'Payment notification received', 'data': data})
if __name__ == '__main__':
app.run(debug=True, host='127.0.0.1', port=5000)
# Turn off debug mode for production
我注意到负载中的某些字段(特别是
f_r_amounts
、s_r_amounts
、f_hou_amount
和 s_hou_amount
)包含 None
值,这可能会导致请求失败。
INFO:werkzeug:127.0.0.1 - - [03/Aug/2024 18:54:19] "OPTIONS /create_order HTTP/1.1" 200 -
DEBUG:root:Request data: {'order_id': '84f1206a-a0c6-4c15-a04c-bf2f260a0229', 'order_amount': '1905', 'customer_id': 'default_customer_id', 'customer_name': '7th SRB', 'customer_email': '[email protected]', 'customer_phone': '+917777777777', 'total_amount': '1905', 'selected_numbers': [25, 71], 'selected_ranges': ['1 H', '7 E', 'A P'], 'hou_heda_values': {'7 E': '350', '1 H': '500', 'A P':
'50'}, 'hou_jeda_values': {'1 H': '400', 'A P': '100', '7 E': '400'}, 'f_r_amounts': [None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, '10', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, '40', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None], 's_r_amounts': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, '20', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, '35', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'f_hou_amount': None, 's_hou_amount': None}
DEBUG:root:Payload: {'order_id': '84f1206a-a0c6-4c15-a04c-bf2f260a0229', 'order_amount': '1905', 'order_currency': 'INR', 'customer_details': {'customer_id':
'default_customer_id', 'customer_name': '7th SRB', 'customer_email': '[email protected]', 'customer_phone': '+917777777777'}, 'order_meta': {'return_url': 'http://localhost:6950/payment_response?order_id=84f1206a-a0c6-4c15-a04c-bf2f260a0229&status=success&user_name=7th%20SRB&user_mobile_number=%2B917777777777&total_amount=1905&selected_numbers=%5B25%2C%2071%5D&selected_ranges=%5B%221%20H%22%2C%20%227%20E%22%2C%20%22A%20P%22%5D&hou_heda_values=%7B%227%20E%22%3A%20%22350%22%2C%20%221%20H%22%3A%20%22500%22%2C%20%22A%20P%22%3A%20%2250%22%7D&hou_jeda_values=%7B%221%20H%22%3A%20%22400%22%2C%20%22A%20P%22%3A%20%22100%22%2C%20%227%20E%22%3A%20%22400%22%7D&f_r_amounts=%5B0%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%20%2210%22%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%20%2240%22%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%5D&s_r_amounts=%5B0%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%20%2220%22%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%20%2235%22%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%5D&f_hou_amount=None&s_hou_amount=None'}}
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): sandbox.cashfree.com:443
DEBUG:urllib3.connectionpool:https://sandbox.cashfree.com:443 "POST /pg/orders HTTP/11" 500 76
DEBUG:root:Response: {'message': 'api Request Failed', 'code': 'request_failed', 'type': 'api_error'}
INFO:werkzeug:127.0.0.1 - - [03/Aug/2024 18:54:20] "POST /create_order HTTP/1.1" 500 -
在将请求发送到 Cashfree 之前,我尝试将列表的 None 值替换为 0,将各个字段替换为空字符串:
f_r_amounts = [0 if v is None else v for v in data.get("f_r_amounts", [])]
s_r_amounts = [0 if v is None else v for v in data.get("s_r_amounts", [])]
f_hou_amount = data.get("f_hou_amount", "")
s_hou_amount = data.get("s_hou_amount", "")
import 'package:flutter/material.dart';
import 'package:flutter_cashfree_pg_sdk/api/cferrorresponse/cferrorresponse.dart';
import 'package:flutter_cashfree_pg_sdk/api/cfpayment/cfwebcheckoutpayment.dart';
import 'package:flutter_cashfree_pg_sdk/api/cfpaymentgateway/cfpaymentgatewayservice.dart';
import 'package:flutter_cashfree_pg_sdk/api/cfsession/cfsession.dart';
import 'package:flutter_cashfree_pg_sdk/utils/cfenums.dart';
import 'package:flutter_cashfree_pg_sdk/utils/cfexceptions.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:uuid/uuid.dart';
class PaymentScreen extends StatelessWidget {
final double totalAmount;
final List<int> selectedNumbers;
final List<String> selectedRanges;
final String? fHouAmount;
final String? sHouAmount;
final Map<String, String?> houHedaValues;
final Map<String, String?> houJedaValues;
final List<String?> fRAmounts;
final List<String?> sRAmounts;
final String userName;
final String userMobileNumber;
final BuildContext context;
const PaymentScreen({
super.key,
required this.totalAmount,
required this.selectedNumbers,
required this.selectedRanges,
required this.houHedaValues,
required this.houJedaValues,
required this.fRAmounts,
required this.sRAmounts,
this.fHouAmount,
this.sHouAmount,
required this.userName,
required this.userMobileNumber,
required this.context,
});
Future<void> createOrderAndInitiatePayment(BuildContext context) async {
final orderId = Uuid().v4();
final orderData = {
"order_id": orderId,
"order_amount": totalAmount.toString(),
"customer_id": 'default_customer_id',
"customer_name": userName,
"customer_email": '[email protected]',
"customer_phone": userMobileNumber,
"total_amount": totalAmount.toString(),
"selected_numbers": selectedNumbers,
"selected_ranges": selectedRanges,
"hou_heda_values": houHedaValues,
"hou_jeda_values": houJedaValues,
"f_r_amounts": fRAmounts,
"s_r_amounts": sRAmounts,
"f_hou_amount": fHouAmount,
"s_hou_amount": sHouAmount,
};
try {
final response = await http.post(
Uri.parse('http://127.0.0.1:5000/create_order'),
headers: {'Content-Type': 'application/json'},
body: json.encode(orderData),
);
if (response.statusCode == 200) {
final responseData = json.decode(response.body);
final String paymentSessionId = responseData['payment_session_id'];
if (paymentSessionId.isNotEmpty) {
_createAndStartPaymentSession(orderId, paymentSessionId, context);
} else {
print('Error: Missing payment_session_id');
}
} else {
print('Failed to create order: ${response.body}');
}
} catch (e) {
print('Error: $e');
}
}
void _createAndStartPaymentSession(
String orderId, String paymentSessionId, BuildContext context) {
try {
CFSession session = CFSessionBuilder()
.setEnvironment(CFEnvironment.SANDBOX) //PRODUCTION/SANDBOX
.setOrderId(orderId)
.setPaymentSessionId(paymentSessionId)
.build();
var cfWebCheckoutBuilder =
CFWebCheckoutPaymentBuilder().setSession(session);
var cfWebCheckout = cfWebCheckoutBuilder.build();
CFPaymentGatewayService().setCallback(
(orderId) => onVerify(orderId, context),
(errorResponse, orderId) => onError(errorResponse, orderId),
);
CFPaymentGatewayService().doPayment(cfWebCheckout);
} on CFException catch (e) {
print(e.message);
}
}
void onVerify(String orderId, BuildContext context) async {
final response = await http.get(
Uri.parse('http://127.0.0.1:5000/payment_response?order_id=$orderId'));
if (response.statusCode == 200) {
final responseData = json.decode(response.body);
final String status = responseData['status'];
final String redirectUrl = responseData['redirect_url'];
if (status == 'success' && redirectUrl == 'image_screen') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageScreen(
status: 'PAYMENT SUCCESSFUL',
),
),
);
} else {
// Handle other statuses or errors
print('Payment not successful');
}
} else {
// Handle HTTP error
print('Failed to verify payment');
}
}
void onError(CFErrorResponse errorResponse, String orderId) {
print("Error while making payment: ${errorResponse.getMessage()}");
// Handle payment error
}
@override
Widget build(BuildContext context) {
DateTime now = DateTime.now();
String formattedDateTime = DateFormat('dd/MM/yyyy hh:mm a').format(now);
return Scaffold(
appBar: AppBar(
title: const Text('Token'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// UI detials here. remeber to uncomment time imports
children: [
Padding(
padding: const EdgeInsets.fromLTRB(70.0, 8.0, 8.0, 8.0),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userName,
style: const TextStyle(
fontSize: 15.0,
),
),
Text(
userMobileNumber,
style: const TextStyle(
fontSize: 15.0,
),
),
],
),
),
Opacity(
opacity: 0.5,
child: Text(
formattedDateTime,
style: const TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold),
),
),
],
),
),
Expanded(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 20.0),
Column(
children: [
const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Num',
style: TextStyle(
fontSize: 18.0,
// fontWeight: FontWeight.bold,
),
),
Text(
'F/R',
style: TextStyle(
fontSize: 18.0,
// fontWeight: FontWeight.bold,
),
),
Padding(
padding: EdgeInsets.only(right: 20.0),
child: Text(
'S/R',
style: TextStyle(
fontSize: 18.0,
),
),
),
],
),
...selectedNumbers.map((number) {
String? frAmount = fRAmounts[number];
String? srAmount = sRAmounts[number];
return ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
child: Text(
number.toString(),
style: const TextStyle(
fontSize: 18.0,
),
),
),
),
const SizedBox(width: 16.0),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
child: Text(
(frAmount != null) ? '₹$frAmount' : '×',
style: const TextStyle(
fontSize: 18.0,
),
),
),
),
const SizedBox(width: 16.0),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
child: Text(
(srAmount != null) ? '₹$srAmount' : '×',
style: const TextStyle(
fontSize: 18.0,
),
),
),
),
],
),
);
}) //.toList(),
],
),
Column(
children: [
...selectedRanges.map((selectedHou) {
String? hedaAmount = houHedaValues[selectedHou];
String? jedaAmount = houJedaValues[selectedHou];
return ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
child: Text(
selectedHou,
style: const TextStyle(
fontSize: 18.0,
),
),
),
),
const SizedBox(width: 16.0),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
child: Text(
hedaAmount != null ? '₹$hedaAmount' : '×',
style: const TextStyle(
fontSize: 18.0,
),
),
),
),
const SizedBox(width: 16.0),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
child: Text(
jedaAmount != null ? '₹$jedaAmount' : '×',
style: const TextStyle(
fontSize: 18.0,
),
),
),
),
],
),
);
}) //.toList(),
],
),
],
),
),
),
],
),
bottomNavigationBar: Container(
padding: const EdgeInsets.all(16.0),
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Total Amount:',
style: TextStyle(fontSize: 20.0),
),
Text(
'₹${totalAmount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
ElevatedButton(
onPressed: () {
createOrderAndInitiatePayment(context);
},
child: const Text('Pay Now'),
),
],
),
),
);
}
}
class ImageScreen extends StatelessWidget {
final String status;
const ImageScreen({super.key, required this.status});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Payment Status'),
),
body: Center(
child: Text(
status, // Display the status text
style: const TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
),
);
}
}
class PaymentResponseHandler extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Uri uri = Uri.base;
final String? status = uri.queryParameters['status'];
final String? orderId = uri.queryParameters['order_id'];
final String? userName = uri.queryParameters['user_name'];
final String? userMobileNumber = uri.queryParameters['user_mobile_number'];
final String? totalAmount = uri.queryParameters['total_amount'];
final String? selectedNumbers = uri.queryParameters['selected_numbers'];
final String? selectedRanges = uri.queryParameters['selected_ranges'];
final String? houHedaValues = uri.queryParameters['hou_heda_values'];
final String? houJedaValues = uri.queryParameters['hou_jeda_values'];
final String? fRAmounts = uri.queryParameters['f_r_amounts'];
final String? sRAmounts = uri.queryParameters['s_r_amounts'];
final String? fHouAmount = uri.queryParameters['f_hou_amount'];
final String? sHouAmount = uri.queryParameters['s_hou_amount'];
if (status == 'success') {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => ImageScreen(
status:
'PAYMENT SUCCESSFUL\nOrder ID: $orderId\nUser: $userName\nPhone: $userMobileNumber\nTotal Amount: $totalAmount\nSelected Numbers: $selectedNumbers\nSelected Ranges: $selectedRanges\nHou Heda Values: $houHedaValues\nHou Jeda Values: $houJedaValues\nF R Amounts: $fRAmounts\nS R Amounts: $sRAmounts\nF Hou Amount: $fHouAmount\nS Hou Amount: $sHouAmount',
),
),
);
});
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
} else {
return Scaffold(
body: Center(
child: Text(
'Payment failed or invalid. Order ID: $orderId',
style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
),
),
);
}
}
}
return_url 太长。请使用较小的并尝试 你现在可以尝试一下 ->
https://www.cashfree.com/devstudio/preview/pg/web/checkout?order_id={order_id}