1use std::collections::{HashMap};
2use base64::Engine;
3use base64::engine::general_purpose;
4use chrono::Local;
5use crate::{PayMode, Types};
6use json::{object, JsonValue};
7use openssl::hash::MessageDigest;
8use openssl::pkey::{PKey};
9use openssl::rsa::Rsa;
10use openssl::sign::Signer;
11use urlencoding::encode;
12
13#[derive(Clone)]
14pub struct AliPay {
15 pub appid: String,
17 pub sp_mchid: String,
19 pub app_auth_token: String,
21 pub app_private: String,
23 pub content_encryp: String,
25}
26impl AliPay {
27 pub fn http(&mut self, method: &str, biz_content: JsonValue) -> Result<JsonValue, String> {
28 let mut http = br_http::Http::new();
29 let sign = "";
30
31 let now = Local::now();
32 let timestamp = now.format("%Y-%m-%d %H:%M:%S").to_string();
33
34
35 let mut data = object! {
36 "charset":"UTF-8",
37 "method":method,
38 "app_id":self.appid.clone(),
39 "version":"1.0",
40 "sign_type":"RSA2",
41 "timestamp":timestamp,
42 "app_auth_token":self.app_auth_token.clone(),
43 "sign":sign,
44 "biz_content": biz_content,
45 };
46
47 let mut map = HashMap::new();
48 for (key, value) in data.entries() {
49 if key == "sign" {
50 continue;
51 }
52 if value.is_empty() {
53 continue;
54 }
55 map.insert(key, value);
56 }
57
58 let mut keys: Vec<_> = map.keys().cloned().collect();
59 keys.sort();
60 let mut txt = vec![];
61 for key in keys {
62 txt.push(format!("{}={}", key, map.get(&key).unwrap()));
63 }
64 let txt = txt.join("&");
65 let t = self.app_private.as_bytes().chunks(64).map(|chunk| std::str::from_utf8(chunk).unwrap_or("")).collect::<Vec<&str>>().join("\n");
66 let cart = format!("-----BEGIN PRIVATE KEY-----\n{}\n-----END PRIVATE KEY-----\n", t);
67
68 let rsa = match Rsa::private_key_from_pem(cart.as_bytes()) {
70 Ok(e) => e,
71 Err(e) => return Err(e.to_string())
72 };
73
74 let pkey = match PKey::from_rsa(rsa) {
75 Ok(e) => e,
76 Err(e) => return Err(e.to_string())
77 };
78
79 let mut signer = match Signer::new(MessageDigest::sha256(), &pkey) {
81 Ok(e) => e,
82 Err(e) => return Err(e.to_string())
83 };
84 match signer.update(txt.as_bytes()) {
85 Ok(()) => {}
86 Err(e) => return Err(e.to_string())
87 };
88 let signature = match signer.sign_to_vec() {
90 Ok(e) => e,
91 Err(e) => return Err(e.to_string())
92 };
93 data["sign"] = general_purpose::STANDARD.encode(signature).into();
95
96 let mut new_data = object! {};
97 for (key, value) in data.entries() {
98 let t = encode(value.to_string().as_str()).to_string();
99 new_data[key] = t.into();
100 }
101 data = new_data;
102 let url = "https://openapi.alipay.com/gateway.do".to_string();
103 let res = match http.post(url.as_str()).query(data).json() {
104 Ok(e) => e,
105 Err(e) => {
106 println!(">>>>>>{}", e);
107 return Err(e);
108 }
109 };
110 if res.has_key("error_response") {
111 return Err(res["error_response"]["sub_msg"].to_string());
112 }
113 let key = method.replace(".", "_");
114 let key = format!("{}_response", key);
115 let data = res[key].clone();
116 if data.has_key("code") {
117 if data["code"] != "10000" {
118 Err(data["sub_msg"].to_string())
119 } else {
120 Ok(data)
121 }
122 } else {
123 Err(data.to_string())
124 }
125 }
126}
127impl PayMode for AliPay {
128 fn config(&mut self) -> JsonValue {
129 todo!()
130 }
131
132 fn login(&mut self, code: &str) -> Result<JsonValue, String> {
133 let res = self.http("alipay.system.oauth.token", object! {
134 code:code
136 })?;
137 Ok(res)
138 }
139
140 fn auth(&mut self, code: &str) -> Result<JsonValue, String> {
141 let res = match self.http("alipay.open.auth.token.app", object! {
142 grant_type:"authorization_code",
143 code:code
144 }) {
145 Ok(e) => e,
146 Err(e) => {
147 println!("Err: {:#}", e);
148 return Err(e);
149 }
150 };
151 println!("{:#}", res);
157 Ok(object! {})
158 }
159
160 fn pay(&mut self, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, notify_url: &str, sp_openid: &str) -> Result<JsonValue, String> {
161 todo!()
162 }
163
164 fn jsapi(
165 &mut self,
166 _sub_mchid: &str,
167 out_trade_no: &str,
168 description: &str,
169 total_fee: f64,
170 _notify_url: &str,
171 sp_openid: &str,
172 ) -> Result<JsonValue, String> {
173 self.app_auth_token = "202503BB5aba0791592f4cd9966ba090d36dfX11".to_string();
174 match self.http("alipay.trade.create", object! {
175 out_trade_no:out_trade_no,
176 total_amount:total_fee,
177 subject:description,
178 product_code:"JSAPI_PAY",
179 op_app_id:"2088541900270114",
180 buyer_open_id:sp_openid
181 }) {
182 Ok(e) => {
183 println!("{:#}", e);
184 }
185 Err(e) => {
186 println!("Err: {:#}", e);
187 return Err(e);
188 }
189 };
190
191
192 Ok(object! {})
193 }
194
195 fn close(&mut self, _out_trade_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
196 todo!()
197 }
198
199 fn pay_query(&mut self, _out_trade_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
200 todo!()
201 }
202
203 fn pay_notify(&mut self, _nonce: &str, _ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
204 todo!()
205 }
206
207 fn refund(&mut self, _sub_mchid: &str, _out_trade_no: &str, _transaction_id: &str, _out_refund_no: &str, _amount: f64, _total: f64, _currency: &str) -> Result<JsonValue, String> {
208 todo!()
209 }
210
211 fn refund_notify(&mut self, _nonce: &str, _ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
212 todo!()
213 }
214
215 fn refund_query(&mut self, _out_refund_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
216 todo!()
217 }
218}