use chrono::{DateTime, FixedOffset, Local, NaiveDateTime};
use crate::wechat::{Wechat};
use json::{object, JsonValue};
use crate::alipay::AliPay;
use crate::allinpay::Allinpay;
use crate::ccbc::Ccbc;
use crate::yrcc::Yrcc;
pub mod alipay;
pub mod wechat;
pub mod ccbc;
pub mod yrcc;
pub mod allinpay;
#[derive(Clone, Debug)]
pub enum Pay {
Wechat(Wechat),
Alipay(AliPay),
Ccbc(Ccbc),
Yrcc(Yrcc),
Allinpay(Allinpay),
None,
}
impl Pay {
pub fn new(data: JsonValue) -> Self {
match data["mode"].as_str().unwrap_or("") {
"wechat" => {
Pay::Wechat(Wechat {
appid: data["appid"].to_string(),
sp_mchid: data["sp_mchid"].to_string(),
serial_no: data["serial_no"].to_string(),
app_private: data["app_private"].to_string(),
apikey: data["apikey"].to_string(),
apiv2: data["apiv2"].to_string(),
notify_url: data["notify_url"].to_string(),
})
}
"alipay" => Pay::Alipay(AliPay {
appid: data["appid"].to_string(),
sp_mchid: data["sp_mchid"].to_string(),
app_private: data["app_private"].to_string(),
app_auth_token: data["app_auth_token"].as_str().unwrap_or("").to_string(),
content_encryp: data["content_encryp"].to_string(),
alipay_public_key: data["alipay_public_key"].to_string(),
notify_url: data["notify_url"].to_string(),
appid_mini: data["appid_mini"].to_string(),
}),
"yrcc" => Pay::Yrcc(Yrcc {
appid: "".to_string(),
secret: "".to_string(),
terminal_number: data["terminal_number"].to_string(),
sp_mchid: data["sp_mchid"].to_string(),
app_private: data["app_private"].to_string(),
app_public: data["app_public"].to_string(),
notify_url: data["notify_url"].as_str().unwrap_or("").to_string(),
key_name: data["key_name"].to_string(),
service_provider: data["service_provider"].to_string(),
}),
"ccbc" => Pay::Ccbc(Ccbc {
appid: data["appid"].as_str().unwrap_or("").to_string(),
branchid: data["branchid"].as_str().unwrap_or("").to_string(),
appid_subscribe: data["appid_subscribe"].as_str().unwrap_or("").to_string(),
sp_mchid: data["sp_mchid"].as_str().unwrap_or("").to_string(),
sp_user_id: data["sp_user_id"].as_str().unwrap_or("").to_string(),
sp_pass: data["sp_pass"].as_str().unwrap_or("").to_string(),
sp_posid: data["sp_posid"].as_str().unwrap_or("").to_string(),
notify_url: data["notify_url"].as_str().unwrap_or("").to_string(),
sub_posid: data["sub_posid"].as_str().unwrap_or("").to_string(),
public_key: data["public_key"].as_str().unwrap_or("").to_string(),
client_ip: data["client_ip"].as_str().unwrap_or("").to_string(),
retry: 0,
debug: data["debug"].as_bool().unwrap_or(false),
query_url: data["query_url"].as_str().unwrap_or("").to_string(),
}),
"allinpay" => Pay::Allinpay(Allinpay {
debug: data["debug"].as_bool().unwrap_or(false),
appid: data["appid"].as_str().unwrap_or("").to_string(),
sp_mchid: data["sp_mchid"].as_str().unwrap_or("").to_string(),
notify_url: data["notify_url"].as_str().unwrap_or("").to_string(),
appid_mini: data["appid_mini"].as_str().unwrap_or("").to_string(),
rsa_private: data["rsa_private"].as_str().unwrap_or("").to_string(),
}),
_ => Pay::None
}
}
}
impl PayMode for Pay {
fn check(&mut self) -> Result<bool, String> {
match self {
Self::Wechat(e) => e.check(),
Self::Alipay(e) => e.check(),
Pay::Allinpay(e) => e.check(),
Self::Ccbc(_) => todo!(),
Self::Yrcc(_) => todo!(),
Self::None => Err("No login data".to_owned()),
}
}
fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String> {
match self {
Self::Wechat(e) => e.get_sub_mchid(sub_mchid),
Self::Alipay(e) => e.get_sub_mchid(sub_mchid),
Pay::None => Err("No login data".to_owned()),
&mut Pay::Ccbc(_) => todo!(),
&mut Pay::Yrcc(_) => todo!(),
&mut Pay::Allinpay(_) => todo!(),
}
}
fn config(&mut self) -> JsonValue {
match self {
Self::Wechat(e) => object! {
sp_mchid:e.sp_mchid.clone(),
appid:e.appid.clone(),
},
Self::Alipay(e) => object! {
appid:e.appid.clone(),
sp_mchid:e.sp_mchid.clone()
},
Pay::None => object! {
appid:"",
sp_mchid:""
},
Pay::Ccbc(e) => object! {
appid:e.appid.clone(),
sp_mchid:e.sp_mchid.clone()
},
Pay::Yrcc(e) => object! {
appid:e.appid.clone(),
sp_mchid:e.sp_mchid.clone()
},
&mut Pay::Allinpay(_) => 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> {
match self {
Self::Wechat(e) => e.pay(channel,
types,
sub_mchid,
out_trade_no,
description,
total_fee,
sp_openid,
),
Self::Alipay(e) => e.pay(channel,
types,
sub_mchid,
out_trade_no,
description,
total_fee,
sp_openid,
),
Pay::None => Err("No login data".to_owned()),
Pay::Ccbc(e) => e.pay(
channel,
types,
sub_mchid,
out_trade_no,
description,
total_fee,
sp_openid,
),
Pay::Yrcc(e) => e.pay(
channel,
types,
sub_mchid,
out_trade_no,
description,
total_fee,
sp_openid,
),
Pay::Allinpay(e) => e.pay(
channel,
types,
sub_mchid,
out_trade_no,
description,
total_fee,
sp_openid,
),
}
}
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> {
match self {
Self::Wechat(e) => e.micropay(
channel,
auth_code,
sub_mchid,
out_trade_no,
description,
total_fee,
org_openid,
ip,
),
Self::Alipay(e) => e.micropay(
channel,
auth_code,
sub_mchid,
out_trade_no,
description,
total_fee,
org_openid,
ip,
),
Pay::Ccbc(e) => e.micropay(
channel,
auth_code,
sub_mchid,
out_trade_no,
description,
total_fee,
org_openid,
ip,
),
Pay::None => Err("No login data".to_owned()),
Pay::Yrcc(_) => todo!(),
Pay::Allinpay(e) => e.micropay(
channel,
auth_code,
sub_mchid,
out_trade_no,
description,
total_fee,
org_openid,
ip,
),
}
}
fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
match self {
Self::Wechat(e) => e.close(
out_trade_no, sub_mchid,
),
Self::Alipay(e) => e.close(
out_trade_no, sub_mchid,
),
Pay::Ccbc(e) => e.close(
out_trade_no, sub_mchid,
),
Pay::None => Err("No login data".to_owned()),
Pay::Yrcc(e) => e.close(
out_trade_no, sub_mchid,
),
Pay::Allinpay(e) => e.close(
out_trade_no, sub_mchid,
),
}
}
fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
match self {
Self::Wechat(e) => e.pay_query(
out_trade_no,
sub_mchid,
),
Self::Alipay(e) => e.pay_query(
out_trade_no,
sub_mchid,
),
Self::Ccbc(e) => e.pay_query(
out_trade_no,
sub_mchid,
),
Pay::None => Err("No login data".to_owned()),
Pay::Yrcc(e) => e.pay_query(
out_trade_no,
sub_mchid,
),
Pay::Allinpay(e) => e.pay_query(
out_trade_no,
sub_mchid,
),
}
}
fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str, channel: &str) -> Result<JsonValue, String> {
match self {
Self::Wechat(e) => e.pay_micropay_query(
out_trade_no,
sub_mchid,
channel,
),
Self::Alipay(e) => e.pay_query(
out_trade_no,
sub_mchid,
),
Pay::Ccbc(e) => e.pay_micropay_query(
out_trade_no,
sub_mchid,
channel,
),
Pay::None => Err("No login data".to_owned()),
Pay::Yrcc(e) => e.pay_micropay_query(
out_trade_no,
sub_mchid,
channel,
),
Pay::Allinpay(e) => e.pay_micropay_query(
out_trade_no,
sub_mchid,
channel,
),
}
}
fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
match self {
Self::Wechat(e) => e.pay_notify(
nonce,
ciphertext,
associated_data,
),
Self::Alipay(e) => e.pay_notify(
nonce,
ciphertext,
associated_data,
),
Pay::None => Err("No login data".to_owned()),
&mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!(),
Pay::Allinpay(e) => e.pay_notify(
nonce,
ciphertext,
associated_data,
),
}
}
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> {
match self {
Self::Wechat(e) => e.refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
),
Self::Alipay(e) => e.refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
),
Self::Ccbc(e) => e.refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
),
Pay::None => Err("No login data".to_owned()),
Pay::Yrcc(e) => e.refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
),
Pay::Allinpay(e) => e.refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
),
}
}
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> {
match self {
Self::Wechat(e) => e.micropay_refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
refund_text,
),
Self::Alipay(e) => e.refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
),
Pay::None => Err("No login data".to_owned()),
Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!(),
Pay::Allinpay(e) => e.micropay_refund(
sub_mchid,
out_trade_no,
transaction_id,
out_refund_no,
amount,
total,
currency,
refund_text,
),
}
}
fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
match self {
Self::Wechat(e) => e.refund_notify(
nonce,
ciphertext,
associated_data,
),
Self::Alipay(e) => e.refund_notify(
nonce,
ciphertext,
associated_data,
),
Pay::None => Err("No login data".to_owned()),
Pay::Ccbc(_) | Pay::Yrcc(_) => todo!(),
Pay::Allinpay(e) => e.refund_notify(
nonce,
ciphertext,
associated_data,
),
}
}
fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
match self {
Self::Wechat(e) => e.refund_query(
trade_no,
out_refund_no,
sub_mchid,
),
Self::Alipay(e) => e.refund_query(
trade_no,
out_refund_no,
sub_mchid,
),
Pay::Ccbc(e) => e.refund_query(
trade_no,
out_refund_no,
sub_mchid),
Pay::None => Err("No login data".to_owned()),
Pay::Yrcc(e) => e.refund_query(
trade_no,
out_refund_no,
sub_mchid),
Pay::Allinpay(e) => e.refund_query(
trade_no,
out_refund_no,
sub_mchid),
}
}
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> {
match self {
Self::Wechat(e) => e.incoming(
business_code,
contact_info,
subject_info,
business_info,
settlement_info,
bank_account_info,
),
Self::Alipay(e) => e.incoming(
business_code,
contact_info,
subject_info,
business_info,
settlement_info,
bank_account_info,
),
Pay::None => Err("No incoming data".to_owned()),
&mut Pay::Ccbc(_) | &mut Pay::Yrcc(_) => todo!(),
Pay::Allinpay(e) => e.incoming(
business_code,
contact_info,
subject_info,
business_info,
settlement_info,
bank_account_info,
),
}
}
}
pub trait PayMode {
fn check(&mut self) -> Result<bool, String>;
fn get_sub_mchid(&mut self, sub_mchid: &str) -> Result<JsonValue, String>;
fn config(&mut self) -> JsonValue;
#[allow(clippy::too_many_arguments)]
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>;
#[allow(clippy::too_many_arguments)]
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>;
fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str, channel: &str) -> Result<JsonValue, String>;
fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
#[allow(clippy::too_many_arguments)]
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>;
#[allow(clippy::too_many_arguments)]
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>;
fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
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>;
}
#[derive(Debug)]
pub struct PayNotify {
trade_type: TradeType,
out_trade_no: String,
sp_mchid: String,
sub_mchid: String,
sp_appid: String,
transaction_id: String,
success_time: i64,
sp_openid: String,
sub_openid: String,
total: f64,
payer_total: f64,
currency: String,
payer_currency: String,
trade_state: TradeState,
}
impl PayNotify {
pub fn json(&self) -> JsonValue {
object! {
"trade_type" => self.trade_type.to_string(),
"out_trade_no" => self.out_trade_no.to_string(),
"sp_mchid" => self.sp_mchid.to_string(),
"sub_mchid" => self.sub_mchid.to_string(),
"sp_appid" => self.sp_appid.to_string(),
"transaction_id" => self.transaction_id.to_string(),
"success_time" => self.success_time,
"sp_openid" => self.sp_openid.to_string(),
"sub_openid" => self.sub_openid.to_string(),
"total" => self.total,
"payer_total" => self.payer_total,
"currency" => self.currency.clone(),
"payer_currency" => self.payer_currency.clone(),
"trade_state" => self.trade_state.to_string(),
}
}
pub fn success_time(datetime: &str) -> i64 {
if datetime.is_empty() {
return 0;
}
let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
datetime.timestamp()
}
pub fn datetime_to_timestamp(datetime: &str, fmt: &str) -> i64 {
let t = NaiveDateTime::parse_from_str(datetime, fmt).unwrap();
let tz = FixedOffset::east_opt(Local::now().offset().local_minus_utc()).unwrap();
t.and_local_timezone(tz).unwrap().timestamp()
}
}
#[derive(Debug)]
pub enum TradeType {
JSAPI,
MICROPAY,
None,
}
impl TradeType {
fn from(code: &str) -> TradeType {
match code {
"JSAPI" => TradeType::JSAPI,
"MICROPAY" => TradeType::MICROPAY,
_ => TradeType::None
}
}
fn to_string(&self) -> &'static str {
match self {
TradeType::JSAPI => "JSAPI",
TradeType::MICROPAY => "MICROPAY",
TradeType::None => ""
}
}
}
#[derive(Debug)]
pub enum TradeState {
SUCCESS,
REFUND,
NOTPAY,
CLOSED,
REVOKED,
USERPAYING,
PAYERROR,
None,
}
impl TradeState {
fn from(code: &str) -> TradeState {
match code {
"SUCCESS" | "TRADE_SUCCESS" | "成功" => TradeState::SUCCESS,
"REFUND" | "已全额退款" | "已部分退款" => TradeState::REFUND,
"NOTPAY" | "WAIT_BUYER_PAY" => TradeState::NOTPAY,
"CLOSED" | "TRADE_CLOSED" => TradeState::CLOSED,
"REVOKED" => TradeState::REVOKED,
"USERPAYING" | "待银行确认" => TradeState::USERPAYING,
"PAYERROR" | "TRADE_FINISHED" | "失败" => TradeState::PAYERROR,
_ => TradeState::None
}
}
fn to_string(&self) -> &'static str {
match self {
TradeState::SUCCESS => "已支付",
TradeState::REFUND => "已退款",
TradeState::NOTPAY => "待支付",
TradeState::CLOSED => "已关闭",
TradeState::REVOKED => "已关闭",
TradeState::USERPAYING => "待支付",
TradeState::PAYERROR => "支付失败",
TradeState::None => "待支付"
}
}
}
#[derive(Debug)]
pub enum RefundStatus {
SUCCESS,
CLOSED,
PROCESSING,
ABNORMAL,
None,
}
impl RefundStatus {
fn from(code: &str) -> RefundStatus {
match code {
"SUCCESS" | "Y" | "REFUND_SUCCESS" => RefundStatus::SUCCESS,
"CLOSED" => RefundStatus::CLOSED,
"PROCESSING" | "N" => RefundStatus::PROCESSING,
"ABNORMAL" => RefundStatus::ABNORMAL,
_ => RefundStatus::None
}
}
fn to_string(&self) -> &'static str {
match self {
RefundStatus::SUCCESS => "已退款",
RefundStatus::None => "未退款",
RefundStatus::CLOSED => "已关闭",
RefundStatus::PROCESSING => "退款中",
RefundStatus::ABNORMAL => "退款异常"
}
}
}
#[derive(Debug)]
pub struct RefundNotify {
out_trade_no: String,
refund_no: String,
sp_mchid: String,
sub_mchid: String,
transaction_id: String,
refund_id: String,
success_time: i64,
total: f64,
refund: f64,
payer_total: f64,
payer_refund: f64,
status: RefundStatus,
}
impl RefundNotify {
pub fn json(&self) -> JsonValue {
object! {
"out_trade_no" => self.out_trade_no.clone(),
"sp_mchid" => self.sp_mchid.clone(),
"sub_mchid" => self.sub_mchid.clone(),
"transaction_id" => self.transaction_id.clone(),
"success_time" => self.success_time,
"total" => self.total,
"refund" => self.refund,
"payer_total" => self.payer_total,
"payer_refund" => self.payer_refund,
"status" => self.status.to_string(),
"refund_no"=>self.refund_no.clone(),
"refund_id"=>self.refund_id.clone()
}
}
}
#[derive(Debug, Clone)]
pub enum Types {
Jsapi,
Native,
H5,
MiniJsapi,
App,
Micropay,
}
impl Types {
pub fn str(self) -> &'static str {
match self {
Types::Jsapi => "jsapi",
Types::Native => "native",
Types::H5 => "h5",
Types::MiniJsapi => "minijsapi",
Types::App => "app",
Types::Micropay => "micropay"
}
}
pub fn from(name: &str) -> Self {
match name {
"jsapi" => Types::Jsapi,
"native" => Types::Native,
"h5" => Types::H5,
"minijsapi" => Types::MiniJsapi,
"app" => Types::App,
"micropay" => Types::Micropay,
_ => Types::Jsapi
}
}
}