use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::auth::Signer;
use crate::config::WxPayConfig;
use crate::error::WxPayResult;
use crate::http::{HttpClient, HttpMethod};
use crate::services::transport::{ServiceTransport, TransportObserver};
#[derive(Debug, Clone, Serialize)]
pub struct ProfitSharingRequest {
pub transaction_id: String,
pub out_order_no: String,
pub receivers: Vec<Receiver>,
pub description: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct QueryProfitSharingRequest {
pub transaction_id: String,
pub out_order_no: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Receiver {
#[serde(rename = "type")]
pub receiver_type: String,
pub account: String,
pub amount: u64,
pub description: String,
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct AddProfitSharingReceiverRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub sub_mchid: Option<String>,
pub appid: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub_appid: Option<String>,
#[serde(rename = "type")]
pub receiver_type: String,
pub account: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(rename = "relation_type")]
pub relation_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_relation: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct DeleteProfitSharingReceiverRequest {
pub appid: String,
#[serde(rename = "type")]
pub receiver_type: String,
pub account: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub_mchid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub_appid: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ProfitSharingResponse {
pub order_id: String,
pub out_order_no: String,
pub transaction_id: String,
pub status: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ProfitSharingReceiverResponse {
pub sub_mchid: Option<String>,
pub appid: Option<String>,
pub sub_appid: Option<String>,
#[serde(rename = "type")]
pub receiver_type: Option<String>,
pub account: Option<String>,
pub name: Option<String>,
#[serde(rename = "relation_type")]
pub relation_type: Option<String>,
pub custom_relation: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ProfitSharingFinishRequest {
pub transaction_id: String,
pub out_order_no: String,
pub description: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ProfitSharingFinishResponse {
pub order_id: String,
pub out_order_no: String,
pub transaction_id: String,
pub status: String,
}
#[allow(dead_code)]
pub struct ProfitSharingService {
config: Arc<WxPayConfig>,
http_client: Arc<dyn HttpClient>,
signer: Arc<dyn Signer>,
transport: ServiceTransport,
}
impl ProfitSharingService {
pub fn new(
config: Arc<WxPayConfig>,
http_client: Arc<dyn HttpClient>,
signer: Arc<dyn Signer>,
) -> Self {
Self::new_with_observer(config.clone(), http_client.clone(), signer.clone(), None)
}
pub fn new_with_observer(
config: Arc<WxPayConfig>,
http_client: Arc<dyn HttpClient>,
signer: Arc<dyn Signer>,
transport_observer: Option<Arc<dyn TransportObserver>>,
) -> Self {
Self {
config: config.clone(),
http_client: http_client.clone(),
signer: signer.clone(),
transport: ServiceTransport::new_with_observer(
config,
http_client,
signer,
transport_observer,
),
}
}
pub async fn create_profit_sharing(
&self,
request: &ProfitSharingRequest,
) -> WxPayResult<ProfitSharingResponse> {
let body = serde_json::to_string(request)?;
self.transport
.request(
HttpMethod::Post,
"/v3/profitsharing/orders",
Some(&body),
"profit_sharing.create_profit_sharing",
)
.await
}
pub async fn create(
&self,
request: &ProfitSharingRequest,
) -> WxPayResult<ProfitSharingResponse> {
self.create_profit_sharing(request).await
}
pub async fn create_order(
&self,
request: &ProfitSharingRequest,
) -> WxPayResult<ProfitSharingResponse> {
self.create_profit_sharing(request).await
}
pub async fn add_receiver(
&self,
request: &AddProfitSharingReceiverRequest,
) -> WxPayResult<ProfitSharingReceiverResponse> {
let body = serde_json::to_string(request)?;
self.transport
.request(
HttpMethod::Post,
"/v3/profitsharing/receivers/add",
Some(&body),
"profit_sharing.add_receiver",
)
.await
}
pub async fn delete_receiver(
&self,
request: &DeleteProfitSharingReceiverRequest,
) -> WxPayResult<ProfitSharingReceiverResponse> {
let body = serde_json::to_string(request)?;
self.transport
.request(
HttpMethod::Post,
"/v3/profitsharing/receivers/delete",
Some(&body),
"profit_sharing.delete_receiver",
)
.await
}
pub async fn query_profit_sharing(
&self,
transaction_id: &str,
out_order_no: &str,
) -> WxPayResult<ProfitSharingResponse> {
let path = format!(
"/v3/profitsharing/orders/{}?transaction_id={}",
out_order_no, transaction_id
);
self.transport
.request(
HttpMethod::Get,
&path,
None,
"profit_sharing.query_profit_sharing",
)
.await
}
pub async fn query(
&self,
request: &QueryProfitSharingRequest,
) -> WxPayResult<ProfitSharingResponse> {
self.query_profit_sharing(&request.transaction_id, &request.out_order_no)
.await
}
pub async fn query_order(
&self,
request: &QueryProfitSharingRequest,
) -> WxPayResult<ProfitSharingResponse> {
self.query_profit_sharing(&request.transaction_id, &request.out_order_no)
.await
}
pub async fn finish_profit_sharing(
&self,
request: &ProfitSharingFinishRequest,
) -> WxPayResult<ProfitSharingFinishResponse> {
let body = serde_json::to_string(request)?;
self.transport
.request(
HttpMethod::Post,
"/v3/profitsharing/finish",
Some(&body),
"profit_sharing.finish_profit_sharing",
)
.await
}
pub async fn finish(
&self,
request: &ProfitSharingFinishRequest,
) -> WxPayResult<ProfitSharingFinishResponse> {
self.finish_profit_sharing(request).await
}
pub async fn finish_order(
&self,
request: &ProfitSharingFinishRequest,
) -> WxPayResult<ProfitSharingFinishResponse> {
self.finish_profit_sharing(request).await
}
}
impl std::fmt::Debug for ProfitSharingService {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProfitSharingService").finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_profit_sharing_request_serialization() {
let request = ProfitSharingRequest {
transaction_id: "1217752501201407033233368018".to_string(),
out_order_no: "P20150806125346".to_string(),
receivers: vec![Receiver {
receiver_type: "MERCHANT_ID".to_string(),
account: "1900000109".to_string(),
amount: 100,
description: "分账".to_string(),
name: None,
}],
description: "分账".to_string(),
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("1217752501201407033233368018"));
assert!(json.contains("P20150806125346"));
}
#[test]
fn test_profit_sharing_response_deserialization() {
let json = r#"{
"order_id": "6110000071100999991182020050700019480101",
"out_order_no": "P20150806125346",
"transaction_id": "1217752501201407033233368018",
"status": "FINISHED"
}"#;
let response: ProfitSharingResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.status, "FINISHED");
assert_eq!(
response.order_id,
"6110000071100999991182020050700019480101"
);
}
#[test]
fn test_go_style_profit_sharing_alias_signatures_exist() {
let _ = ProfitSharingService::create_order;
let _ = ProfitSharingService::query_order;
let _ = ProfitSharingService::finish_order;
}
}