use json::{object, JsonValue};
use crate::{PayMode, Types};
#[derive(Clone, Debug)]
pub struct Ccbc {
pub appid: String,
pub secret: String,
pub sp_mchid: String,
pub notify_url: String,
pub posid: String,
pub branchid: String,
pub smername: String,
pub smertypeid: String,
pub smertype: String,
}
impl Ccbc {
pub fn http(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
let mut mac = vec![];
for (key, value) in body.entries() {
if value.is_empty() {
continue;
}
mac.push(format!("{key}={value}"));
}
let mac = mac.join("&");
body["MAC"] = br_crypto::md5::encrypt_hex(mac.as_bytes()).into();
let mac = format!("{}&MAC={}", mac, body["MAC"]);
let mut http = br_reqwest::Client::new();
let url = format!("{url}&{mac}");
let send = http.post(url.as_str()).raw_json(body);
let res = send.header("Content-Type", "application/json").send()?;
match res.content_type().as_str() {
"text/html" => {
Err(res.body().to_string())
}
_ => {
match res.json() {
Ok(e) => Ok(e),
Err(e) => Err(e)
}
}
}
}
fn escape_unicode(&mut self, s: &str) -> String {
s.chars().map(|c| {
if c.is_ascii() {
c.to_string()
} else {
format!("%u{:04X}", c as u32)
}
}).collect::<String>()
}
fn _unescape_unicode(&mut self, s: &str) -> String {
let mut output = String::new();
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == '%' && chars.peek() == Some(&'u') {
chars.next(); let codepoint: String = chars.by_ref().take(4).collect();
if let Ok(value) = u32::from_str_radix(&codepoint, 16) {
if let Some(ch) = std::char::from_u32(value) {
output.push(ch);
}
}
} else {
output.push(c);
}
}
output
}
}
impl PayMode for Ccbc {
fn check(&mut self) -> Result<bool, String> {
todo!()
}
fn get_sub_mchid(&mut self, _sub_mchid: &str) -> Result<JsonValue, String> {
todo!()
}
fn notify(&mut self, _data: JsonValue) -> Result<JsonValue, String> {
todo!()
}
fn config(&mut self) -> JsonValue {
todo!()
}
fn login(&mut self, _code: &str) -> Result<JsonValue, String> {
todo!()
}
fn auth(&mut self, _code: &str) -> Result<JsonValue, String> {
todo!()
}
fn pay(&mut self, channel: &str, types: Types, sub_mchid: &str, out_trade_no: &str, _description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
let mut body = object! {
MERCHANTID:self.sp_mchid.clone(),
POSID:self.posid.clone(),
BRANCHID:self.branchid.clone(),
ORDERID:out_trade_no,
PAYMENT:total_fee,
CURCODE:"01",
TXCODE:"",
MAC:"",
PUB:"21313123213"
};
let url = match channel {
"wechat" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
"alipay" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
_ => return Err(format!("Invalid channel: {channel}")),
};
match channel {
"wechat" => {
body["TXCODE"] = "530590".into();
body["TYPE"] = "1".into();
body["GATEWAY"] = "0".into();
body["TRADE_TYPE"] = match types {
Types::Jsapi => "JSAPI",
Types::MiniJsapi => "MINIPRO",
_ => return Err(format!("Invalid channel: {types:?}")),
}.into();
body["SUB_APPID"] = self.appid.clone().into();
body["SUB_OPENID"] = sp_openid.into();
body["SMERID"] = sub_mchid.into();
body["SMERNAME"] = self.escape_unicode(self.smername.clone().as_str()).into();
body["SMERTYPEID"] = self.smertypeid.clone().into();
body["SMERTYPE"] = self.escape_unicode(self.smertype.clone().as_str()).into();
body["TRADECODE"] = "交易类型代码".into();
body["TRADENAME"] = self.escape_unicode("消费").into();
body["SMEPROTYPE"] = "商品类别代码".into();
body["PRONAME"] = self.escape_unicode("商品").into();
}
"alipay" => {
body["TXCODE"] = "530591".into();
body["TRADE_TYPE"] = match types {
Types::Jsapi => "JSAPI",
Types::MiniJsapi => "JSAPI",
_ => return Err(format!("Invalid channel: {types:?}")),
}.into();
body["USERID"] = sp_openid.into();
}
_ => return Err(format!("Invalid channel: {channel}")),
}
let res = self.http(url, body)?;
Ok(res)
}
fn micropay(&mut self, channel: &str, auth_code: &str, _sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, _org_openid: &str, _ip: &str) -> Result<JsonValue, String> {
let mut body = object! {
MERCHANTID:self.sp_mchid.clone(),
POSID:self.posid.clone(),
BRANCHID:self.branchid.clone(),
ccbParam:"",
TXCODE:"PAY100",
MERFLAG:"1",
ORDERID:out_trade_no,
QRCODE:auth_code,
AMOUNT:total_fee,
PROINFO:"商品名称",
REMARK1:description
};
let url = match channel {
"wechat" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
"alipay" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
_ => return Err(format!("Invalid channel: {channel}")),
};
match channel {
"wechat" => {
body["SUB_APPID"] = self.appid.clone().into();
}
"alipay" => {
}
_ => return Err(format!("Invalid channel: {channel}")),
}
let res = self.http(url, body)?;
Ok(res)
}
fn close(&mut self, _out_trade_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
todo!()
}
fn pay_query(&mut self, _out_trade_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
todo!()
}
fn pay_micropay_query(&mut self, _out_trade_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
todo!()
}
fn pay_notify(&mut self, _nonce: &str,_ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
todo!()
}
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> {
todo!()
}
fn micropay_refund(&mut self, _sub_mchid: &str, _out_trade_no: &str, _transaction_id: &str, _out_refund_no: &str, _amount: f64, _total: f64, _currency: &str, _refund_text: &str) -> Result<JsonValue, String> {
todo!()
}
fn refund_notify(&mut self, _nonce: &str, _ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
todo!()
}
fn refund_query(&mut self, _trade_no: &str, _out_refund_no: &str, _sub_mchid: &str) -> Result<JsonValue, String> {
todo!()
}
fn incoming(&mut self, _business_code: &str, _contact_info: JsonValue, _subject_info: JsonValue, _business_info: JsonValue, _settlement_info: JsonValue, _bank_account_info: JsonValue) -> Result<JsonValue, String> {
todo!()
}
}