diff --git a/Cargo.toml b/Cargo.toml index c2ae9db..a17ee6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "labrador" -version = "0.1.7" +version = "0.1.8" authors = ["mrpan <1049058427@qq.com>"] edition = "2018" description = "Labrador - Mini thirdpart client for rust." diff --git a/src/wechat/mp/api/card.rs b/src/wechat/mp/api/card.rs new file mode 100644 index 0000000..8e1c5aa --- /dev/null +++ b/src/wechat/mp/api/card.rs @@ -0,0 +1,802 @@ +use std::vec; + +use serde::{Serialize, Deserialize, Serializer}; +use serde_json::{json, Value}; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult, LabraError, get_timestamp, TicketType, get_nonce_str, WeChatCrypto, BaseInfo, AdvancedInfo}; +use crate::wechat::mp::constants::{QR_CODE}; +use crate::wechat::mp::method::{MpCardMethod, WechatMpMethod}; + +/// 卡券相关. +#[derive(Debug, Clone)] +pub struct WeChatMpCard<'a, T: SessionStore> { + client: &'a WeChatMpClient, +} + +#[allow(unused)] +impl<'a, T: SessionStore> WeChatMpCard<'a, T> { + + #[inline] + pub fn new(client: &WeChatMpClient) -> WeChatMpCard { + WeChatMpCard { + client, + } + } + + ///
+    /// 获得卡券api_ticket,不强制刷新卡券api_ticket.
+    /// 
+ pub async fn get_card_api_ticket(&self) -> LabradorResult { + self.get_card_api_ticket_force(false).await + } + + ///
+    /// 获得卡券api_ticket.
+    /// 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
+    /// 
+ pub async fn get_card_api_ticket_force(&self, force_refresh: bool) -> LabradorResult { + self.client.get_ticket_force(TicketType::WxCard, force_refresh).await + } + + ///
+    /// 创建调用卡券api时所需要的签名
+    /// 
+ pub async fn create_card_api_signature(&self, mut params: Vec) -> LabradorResult { + let timestamp = get_timestamp() / 1000; + let noncestr = get_nonce_str(); + let api_ticket = self.get_card_api_ticket_force(false).await?; + params.push(timestamp.to_string()); + params.push(noncestr.to_string()); + params.push(api_ticket); + params.sort(); + let signature = WeChatCrypto::get_sha1_sign(¶ms.join("")); + Ok(WechatMpCardApiSignature{ + app_id: self.client.appid.to_string(), + card_id: "".to_string(), + card_type: "".to_string(), + location_id: "".to_string(), + code: "".to_string(), + openid: "".to_string(), + nonce_str: noncestr, + signature, + timestamp, + }) + } + + ///
+    /// 卡券Code解码
+    /// 
+ pub async fn decrypt_card_code(&self, encrypt_code: &str) -> LabradorResult { + let req = json!({ + "encrypt_code": encrypt_code + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::CodeDecrypt), vec![], req, RequestType::Json).await?.json::()?; + let v= WechatCommonResponse::parse::(v)?; + let code = v["code"].as_str().unwrap_or_default(); + Ok(code.to_string()) + } + + ///
+    /// 卡券Code查询.
+    /// 文档地址: 文档
+    /// 
+ pub async fn query_card_code(&self, card_id: &str, code: &str, check_consume: bool) -> LabradorResult { + let req = json!({ + "card_id": card_id, + "code": code, + "check_consume": check_consume, + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::CodeGet), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 卡券Code核销。核销失败会抛出异常
+    /// 
+ pub async fn consume_card_code(&self, code: &str) -> LabradorResult { + self.consume_card_code_with_cardid(None, code).await + } + + ///
+    /// 卡券Code核销。核销失败会抛出异常
+    /// 
+ pub async fn consume_card_code_with_cardid(&self, card_id: Option<&str>, code: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + "code": code, + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::CodeConsume), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 卡券Mark接口.
+    /// 开发者在帮助消费者核销卡券之前,必须帮助先将此code(卡券串码)与一个openid绑定(即mark住),
+    /// 才能进一步调用核销接口,否则报错。
+    /// 
+ pub async fn mark_card_code(&self, code: &str, card_id: &str, openid: &str, is_mark: bool) -> LabradorResult { + let req = json!({ + "card_id": card_id, + "code": code, + "openid": openid, + "is_mark": is_mark, + }); + self.client.post(WechatMpMethod::Card(MpCardMethod::CodeMark), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 查看卡券详情接口.
+    /// 详见 文档
+    /// 
+ pub async fn get_card_detail(&self, card_id: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + }); + let v= self.client.post(WechatMpMethod::Card(MpCardMethod::Get), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 添加测试白名单
+    /// 
+ pub async fn add_test_white_list(&self, openid: &str) -> LabradorResult { + let req = json!({ + "openid": vec![openid], + }); + self.client.post(WechatMpMethod::Card(MpCardMethod::SetWhiteList), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 创建卡券
+    /// 
+ pub async fn create_card(&self, req: WechatMpCardCreateRequest) -> LabradorResult { + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::Create), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 创建卡券二维码
+    /// 
+ pub async fn create_qrcode_card(&self, card_id: &str, outer_str: &str) -> LabradorResult { + self.create_qrcode_card_expire(card_id, outer_str, 0).await + } + + ///
+    /// 创建卡券二维码
+    /// 
+ pub async fn create_qrcode_card_expire(&self, card_id: &str, outer_str: &str, expires_in: i64) -> LabradorResult { + self.create_qrcode_card_complex(card_id, outer_str, expires_in, None, None, false).await + } + + ///
+    /// 创建卡券二维码
+    /// 
+ pub async fn create_qrcode_card_complex(&self, card_id: &str, outer_str: &str, expires_in: i64, openid: Option<&str>, code: Option<&str>, is_unique_code: bool) -> LabradorResult { + let mut req = json!({ + "action_name" : QR_CODE, + }); + if expires_in > 0 { + req["expire_seconds"] = expires_in.into(); + } + let action_info = json!({ + "card": { + "openid": openid, + "code": code, + "is_unique_code": is_unique_code, + "card_id": card_id, + "outer_str": outer_str, + } + }); + req["action_info"] = action_info; + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::CreateQrcode), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 创建卡券货架
+    /// 
+ pub async fn create_landing_page(&self, req: WechatMpCardLandingPageCreateRequest) -> LabradorResult { + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::CreateLandingpage), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 将用户的卡券设置为失效状态.
+    /// 详见:文档
+    /// 
+ pub async fn unavailable_card_code(&self, card_id: &str, code: &str, reason: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + "code": code, + "reason": reason + }); + self.client.post(WechatMpMethod::Card(MpCardMethod::UnavailabeCode), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 删除卡券接口
+    /// 
+ pub async fn delete_card(&self, card_id: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + }); + self.client.post(WechatMpMethod::Card(MpCardMethod::Delete), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 导入自定义code(仅对自定义code商户)
+    /// 
+ pub async fn card_code_deposit(&self, card_id: &str, code_list: Vec<&str>) -> LabradorResult { + if code_list.len() == 0 || code_list.len() > 100 { + return Err(LabraError::RequestError("code数量为0或者code数量超过100个".to_string())) + } + let req = json!({ + "card_id": card_id, + "code": code_list, + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::CodeDeposit), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 查询导入code数目接口
+    /// 
+ pub async fn card_code_deposit_count(&self, card_id: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::GetDepositCount), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 核查code接口
+    /// 
+ pub async fn card_code_check_code(&self, card_id: &str, code_list: Vec<&str>) -> LabradorResult { + if code_list.len() == 0 || code_list.len() > 100 { + return Err(LabraError::RequestError("code数量为0或者code数量超过100个".to_string())) + } + let req = json!({ + "card_id": card_id, + "code": code_list, + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::CheckCode), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 图文消息群发卡券获取内嵌html
+    /// 
+ pub async fn card_mpnews_get_html(&self, card_id: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::GetHtml), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 修改库存接口
+    /// 文档
+    /// 
+ pub async fn card_modify_stock(&self, card_id: &str, change_value: i64) -> LabradorResult { + let mut req = json!({ + "card_id": card_id, + }); + if change_value > 0 { + req["increase_stock_value"] = change_value.into(); + } else { + req["reduce_stock_value"] = change_value.abs().into(); + } + self.client.post(WechatMpMethod::Card(MpCardMethod::ModifyStock), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 更改Code接口
+    /// 文档
+    /// 
+ pub async fn card_code_update(&self, card_id: &str, old_code: &str, new_code: &str) -> LabradorResult { + let mut req = json!({ + "card_id": card_id, + "code": old_code, + "new_code": new_code, + }); + self.client.post(WechatMpMethod::Card(MpCardMethod::UpdateCode), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 设置买单接口
+    /// 文档
+    /// 
+ pub async fn card_paycell_set(&self, card_id: &str, is_open: bool) -> LabradorResult { + let mut req = json!({ + "card_id": card_id, + "is_open": is_open, + }); + self.client.post(WechatMpMethod::Card(MpCardMethod::SetPayCell), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 设置自助核销
+    /// 文档
+    /// 
+ pub async fn card_self_consume_cell_set(&self, card_id: &str, is_open: bool, need_verify_cod: bool, need_remark_amount: bool) -> LabradorResult { + let mut req = json!({ + "card_id": card_id, + "is_open": is_open, + "need_verify_cod": need_verify_cod, + "need_remark_amount": need_remark_amount, + }); + self.client.post(WechatMpMethod::Card(MpCardMethod::SetSelfConsumerCell), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 获取用户已领取卡券接口
+    /// 文档
+    /// 
+ pub async fn get_user_card_list(&self, card_id: &str, openid: &str) -> LabradorResult { + let mut req = json!({ + "card_id": card_id, + "openid": openid, + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::GetUserCardList), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + /// 为了降低商户接入卡券的难度,微信公众平台向所有已具备卡券功能的公众号开放“第三方代制”功能。申请并开通此功能后,具备开发能力的开发者,可通过 API 接口协助无公众号的商户快速接入并使用卡券。协助制券的开发者称为“母商户”,被协助制券的商户称为“子商户”。 + /// + /// 母商户需将旗下子商户资料提前上传报备,通过审核方可生效。在制券过程中允许母商户从报备的子商户列表中,选择一个子商户协助制券。 + /// + /// 开通步骤 + /// + /// 第一步,申请路径:微信公众平台 - 卡券功能 - 右上角 - 商户信息 - 第三方代制模式。 + /// + /// 第二步,商户通过微信公众平台或 API 接口,提交子商户资料、资质,审核通过后可使用该子商户信息制券。 + /// + /// 第三步,调用 API 接口创建卡券时,需传入该模式的特有字段,具体字段参考创建子商户接口的返回字段说明。该模式下,仅创建卡券接口有变动,其余接口和卡券整体接口的使用保持不变。详情参考首页 + ///
+    /// 创建子商户接口
+    /// 文档
+    /// 支持母商户调用该接口传入子商户的相关资料,并获取子商户ID,用于子商户的卡券功能管理。 子商户的资质包括:商户名称、商户logo(图片)、卡券类目、授权函(扫描件或彩照)、授权函有效期截止时间。
+    /// 备注:授权函请在 (《第三方代制模式指引文档》)[https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1459357007&version=1&lang=zh_CN&platform=2]内下载,手填并加盖鲜章后,上传彩色扫描件或彩照。
+    ///
+    /// 1、授权函必须加盖企业公章,或个体户店铺章、发票专用章、财务章、合同章等具备法律效力的盖章,不可使用个人私章;
+    ///
+    /// 2、若子商户是个体工商户,且无上述公章,授权函可用个体工商户经营者签字代替公章,且须同时额外上传《个体工商户营业执照》及该执照内登记的经营者的身份证彩照。(本方案仅适用于子商户是个体工商户,且无公章的场景。其他场景必须在授权函加盖公章)
+    ///
+    /// 3、子商户若有公众号,且不愿意自己运营,通过授权方式让第三方代制,支持配置子商户公众号。配置后,1)该子商户的制券配额不再限制,2)该卡券详情页关联的公众号为子商户配置这个公众号。
+    /// 
+ pub async fn create_submerchant(&self, req: WechatMpCardCreateSubmerchantRequest) -> LabradorResult { + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::SubmitSubmerchant), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 卡券开放类目查询接口
+    /// 文档
+    /// 通过调用该接口查询卡券开放的类目ID,类目会随业务发展变更,请每次用接口去查询获取实时卡券类目。
+    ///
+    /// 注意:
+    ///
+    /// 1.本接口查询的返回值还有卡券资质 ID ,此处的卡券资质为:已微信认证的公众号通过微信公众平台申请卡券功能时,所需的资质。
+    ///
+    /// 2.对于第三方开发者代制(无公众号)模式,子商户无论选择什么类目,均暂不需按照此返回提供资质,返回值仅参考类目ID 即可。
+    /// 
+ pub async fn get_cateogry(&self) -> LabradorResult { + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::GetApplyProtocol), vec![], Value::Null, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + + ///
+    /// 更新子商户接口
+    /// 文档
+    /// 支持调用该接口更新子商户信息。
+    /// 
+ pub async fn update_submerchant(&self, req: WechatMpCardCreateSubmerchantRequest) -> LabradorResult { + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::UpdateSubmerchant), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + + + ///
+    /// 拉取单个子商户信息接口
+    /// 文档
+    /// 通过指定的子商户appid,拉取该子商户的基础信息。 注意,用母商户去调用接口,但接口内传入的是子商户的appid。
+    /// 
+ pub async fn get_submerchant(&self, merchant_id: i32) -> LabradorResult { + let req = json!({ + "merchant_id": merchant_id + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::GetSubmerchant), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 批量拉取子商户信息接口
+    /// 文档
+    /// 母商户可以通过该接口批量拉取子商户的相关信息,一次调用最多拉取100个子商户的信息,可以通过多次拉去满足不同的查询需求
+    /// 
+ pub async fn get_submerchant_batch(&self, begin_id: i64, limit: i64, status: &str) -> LabradorResult { + let req = json!({ + "begin_id": begin_id, + "limit": limit, + "status": status + }); + let v = self.client.post(WechatMpMethod::Card(MpCardMethod::BatchGetSubmerchant), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } +} + +//---------------------------------------------------------------------------------------------------------------------------- +/// 卡券Api签名 +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardApiSignature { + pub app_id: String, + pub card_id: String, + pub card_type: String, + pub location_id: String, + pub code: String, + pub openid: String, + #[serde(rename="nonceStr")] + pub nonce_str: String, + pub signature: String, + pub timestamp: i64, +} + +/// 卡券查询Code,核销Code接口返回结果. +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardResponse { + pub card: WechatMpCard, + pub user_card_status: Option, + pub can_consume: bool, + pub out_str: Option, + pub background_pic_url: Option, + pub openid: String, + pub unionid: Option, +} + +/// 微信卡券 +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCard { + pub card_id: String, + pub begin_time: i64, + pub end_time: i64, + pub user_card_status: Option, + pub membership_number: Option, + pub code: Option, + pub bonus: Option, +} + +/// 核销返回 +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardCodeConsumeResponse { + pub card: CodeConsumeCard, + pub openid: String, +} +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct CodeConsumeCard { + pub card_id: String, +} + + +#[allow(unused)] +pub struct WechatMpCardCreateRequest { + pub card: AbstractCardCreateRequest, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct GrouponCardCreateRequest { + pub card_type: String, + pub groupon: GrouponCard, +} + +#[derive(Serialize, Deserialize)] +pub struct GrouponCard { + /// 团购券专用,团购详情 + pub deal_detail: String, + /// 基本信息 + pub base_info: BaseInfo, + /// 基本信息 + pub advanced_info: AdvancedInfo, +} +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct GiftCardCreateRequest { + pub card_type: String, + pub gift: GiftCard, +} + +#[derive(Serialize, Deserialize)] +pub struct GiftCard { + /// 兑换券专用,填写兑换内容的名称。 + pub gift: String, + /// 基本信息 + pub base_info: BaseInfo, + /// 基本信息 + pub advanced_info: AdvancedInfo, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct GeneralCouponCreateRequest { + pub card_type: String, + pub general_coupon: GeneralCoupon, +} + +#[derive(Serialize, Deserialize)] +pub struct GeneralCoupon { + /// 兑换券专用,填写兑换内容的名称。 + pub default_detail: String, + /// 基本信息 + pub base_info: BaseInfo, + /// 基本信息 + pub advanced_info: AdvancedInfo, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct DiscountCardCreateRequest { + pub card_type: String, + pub discount: DiscountCard, +} + +#[derive(Serialize, Deserialize)] +pub struct DiscountCard { + /// 折扣券专用,表示打折额度(百分比)。填30就是七折。 + pub discount: i32, + /// 基本信息 + pub base_info: BaseInfo, + /// 基本信息 + pub advanced_info: AdvancedInfo, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct CashCardCreateRequest { + pub card_type: String, + pub cash: CashCard, +} + +#[derive(Serialize, Deserialize)] +pub struct CashCard { + /// 代金券专用,表示起用金额(单位为分),如果无起用门槛则填0 + pub least_cost: i32, + /// 代金券专用,表示减免金额。(单位为分) + pub reduce_cost: i32, + /// 基本信息 + pub base_info: BaseInfo, + /// 基本信息 + pub advanced_info: AdvancedInfo, +} + +pub enum AbstractCardCreateRequest { + Cash(CashCardCreateRequest), + Discount(DiscountCardCreateRequest), + GeneralCoupon(GeneralCouponCreateRequest), + Gift(GiftCardCreateRequest), + Groupon(GrouponCardCreateRequest), +} + +impl Serialize for WechatMpCardCreateRequest { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + match &self.card { + AbstractCardCreateRequest::Cash(v) => v.serialize(serializer), + AbstractCardCreateRequest::Discount(v) => v.serialize(serializer), + AbstractCardCreateRequest::GeneralCoupon(v) => v.serialize(serializer), + AbstractCardCreateRequest::Gift(v) => v.serialize(serializer), + AbstractCardCreateRequest::Groupon(v) => v.serialize(serializer), + } + } +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardCreateResponse { + pub card_id: String, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardQrcodeCreateResponse { + pub ticket: String, + pub url: Option, + pub show_qrcode_url: Option, + pub expire_seconds: i64, +} +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardLandingPageCreateRequest { + /// 页面的banner图片链接,须调用,建议尺寸为640*300。 + pub banner: String, + /// 页面的title + pub page_title: String, + /// 投放页面的场景值; + /// SCENE_NEAR_BY 附近 + /// SCENE_MENU 自定义菜单 + /// SCENE_QRCODE 二维码 + /// SCENE_ARTICLE 公众号文章 + /// SCENE_H5 h5页面 + /// SCENE_IVR 自动回复 + /// SCENE_CARD_CUSTOM_CELL 卡券自定义cell + pub scene: String, + pub can_share: bool, + pub card_list: Vec, +} +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct CardLandingPage { + pub card_id: String, + pub thumb_url: String, +} + + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardLandingPageCreateResponse { + /// 货架链接 + pub url: String, + /// 货架ID。货架的唯一标识 + pub page_id: i32, +} + + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardCodeDepositResponse { + /// 成功的code + pub succ_code: Vec, + /// 重复导入的code + pub duplicate_code: Vec, + /// 失败的code + pub fail_code: Vec, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardCheckCodeResponse { + /// 已经成功存入的code数目 + pub exist_code: Vec, + pub not_exist_code: Vec, +} + + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardCodeDepositCountResponse { + /// 已经成功存入的code数目 + pub count: i32, +} +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardMpnewsGethtmlResponse { + /// 返回一段html代码,可以直接嵌入到图文消息的正文里。即可以把这段代码嵌入到 上传图文消息素材接口 中的content字段里 + pub content: String, +} + +/// 用户已领卡券返回 +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatUserCardListResponse { + /// 卡券列表 + pub card_list: Vec, + /// 是否有可用的朋友的券 + pub has_share_card: bool, +} +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct UserCard { + /// 用户卡券code码 + pub code: String, + /// 卡券ID + pub card_id: String, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardCreateSubmerchantRequest { + pub info: CreateSubmerchantRequest, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct CreateSubmerchantRequest { + /// 子商户id,一个母商户公众号下唯一 + pub merchant_id: Option, + /// 子商户的公众号app_id,配置后子商户卡券券面上的app_id为该app_id。注意:该app_id须经过认证 + pub app_id: Option, + /// 子商户名称(12个汉字内),该名称将在制券时填入并显示在卡券页面上 + pub brand_name: String, + /// 子商户logo,可通过 上传图片接口 获取。该 logo 将在制券时填入并显示在卡券页面上 + pub logo_url: String, + /// 授权函ID,即通过 上传临时素材接口 上传授权函后获得的media_id + pub protocol: String, + /// 授权函有效期截止时间(东八区时间,单位为秒),需要与提交的扫描件一致 + pub end_time: i64, + /// 一级类目 id ,可以通过本文档中接口查询 + pub primary_category_id: u8, + /// 二级类目id,可以通过本文档中接口查询 + pub secondary_category_id: u8, + /// 营业执照或个体工商户营业执照彩照或扫描件 + pub agreement_media_id: Option, + /// 营业执照内登记的经营者身份证彩照或扫描件 + pub operator_media_id: Option, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardSubmerchantResponse { + pub info: SubmerchantResponse, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardSubmerchantBatchResponse { + pub info_list: Vec, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct SubmerchantResponse { + /// 子商户id,对于一个母商户公众号下唯一 + pub merchant_id: Option, + ///子商户若有公众号,且不愿意自己运营,通过授权方式让第三方代制,支持配置子商户公众号。配置后, + /// 1)该子商户的制券配额不再限制, + /// 2)该卡券详情页关联的公众号为子商户配置这个公众号。 + pub app_id: Option, + /// 子商户信息创建时间 + pub create_time: Option, + /// 子商户信息更新时间 + pub update_time: Option, + /// 子商户名称(12个汉字内),该名称将在制券时填入并显示在卡券页面上 + pub brand_name: String, + /// 子商户logo,可通过 上传图片接口 获取。该 logo 将在制券时填入并显示在卡券页面上 + pub logo_url: String, + /// 子商户状态,"CHECKING" 审核中, "APPROVED" , 已通过;"REJECTED"被驳回, "EXPIRED"协议已过期 + pub status: String, + /// 创建时间(非协议开始时间) + pub bengin_time: Option, + /// 授权函有效期截止时间(东八区时间,单位为秒) + pub end_time: Option, + /// 一级类目 id ,可以通过本文档中接口查询 + pub primary_category_id: Option, + /// 二级类目id,可以通过本文档中接口查询 + pub secondary_category_id: Option, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct WechatMpCardCategoryResponse { + /// 子商户id,对于一个母商户公众号下唯一 + pub category: Vec, +} +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct CardCategory { + /// 一级类目 id ,可以通过本文档中接口查询 + pub primary_category_id: Option, + /// 分类名称 + pub category_name: Option, + pub secondary_category: Vec, +} + +#[allow(unused)] +#[derive(Serialize, Deserialize)] +pub struct SecondaryCategory { + /// 分类名称 + pub category_name: Option, + /// 二级类目id,可以通过本文档中接口查询 + pub secondary_category_id: Option, + pub can_choose_prepaid_card: Option, + pub can_choose_payment_card: Option, + pub need_qualification_stuffs: Option>, +} + diff --git a/src/wechat/mp/api/member.rs b/src/wechat/mp/api/member.rs new file mode 100644 index 0000000..e270a29 --- /dev/null +++ b/src/wechat/mp/api/member.rs @@ -0,0 +1,979 @@ +use std::vec; + +use serde::{Serialize, Deserialize}; +use serde_json::{json, Value}; + +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, WeChatMpClient, LabradorResult, LabraError, get_timestamp}; +use crate::wechat::mp::constants::MEMBER_CARD; +use crate::wechat::mp::method::{MpMemeberCardMethod, WechatMpMethod}; + +/// 会员卡相关. +#[derive(Debug, Clone)] +pub struct WeChatMpMember<'a, T: SessionStore> { + client: &'a WeChatMpClient, +} + +#[allow(unused)] +impl<'a, T: SessionStore> WeChatMpMember<'a, T> { + + #[inline] + pub fn new(client: &WeChatMpClient) -> WeChatMpMember { + WeChatMpMember { + client, + } + } + + ///
+    /// 会员卡创建接口
+    /// 
+ pub async fn create_member_card_custom(&self, req: D) -> LabradorResult { + let v = serde_json::to_value(req)?; + let req = serde_json::from_value::(v)?; + self.create_member_card(req).await + } + + ///
+    /// 会员卡创建接口
+    /// 
+ pub async fn create_member_card(&self, req: WechatMpMemberCardCreateRequest) -> LabradorResult { + req.valid_check()?; + let v = self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::Create), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 会员卡激活接口
+    /// 
+ pub async fn activate_member_card(&self, req: WechatMpMemberCardActivateRequest) -> LabradorResult { + self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::Activate), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 拉取会员信息接口
+    /// 
+ pub async fn get_user_info(&self, card_id: &str, code: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + "code": code + }); + let v = self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::GetUserInfo), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 当会员持卡消费后,支持开发者调用该接口更新会员信息.
+    /// 会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。
+    /// 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。
+    /// 同时传入add_bonus和bonus时 add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。
+    /// 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。
+    /// 
+ pub async fn update_user_member_card(&self, req: WechatMpMemberCardUpdateRequest) -> LabradorResult { + let v = self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::UpdateUser), vec![], req, RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } + + ///
+    /// 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要).
+    /// 
+ pub async fn set_activate_user_form(&self, req: WechatMpMemberCardActivateUserFormRequest) -> LabradorResult { + self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::ActivateSetUser), vec![], req, RequestType::Json).await?.json::() + } + + ///
+    /// 获取会员卡开卡插件参数(跳转型开卡组件需要参数).
+    /// 
+ pub async fn get_activate_plugin_param(&self, card_id: &str, out_str: &str) -> LabradorResult { + let url = self.get_activate_plugin_url(card_id, out_str).await?; + let decode_url = urlencoding::encode(&url); + let mut params = serde_urlencoded::from_str::(decode_url.as_ref())?; + params.biz = params.biz + "=="; + Ok(params) + } + + ///
+    /// 获取开卡组件链接接口
+    /// 
+ pub async fn get_activate_plugin_url(&self, card_id: &str, out_str: &str) -> LabradorResult { + let req = json!({ + "card_id": card_id, + "outer_str": out_str + }); + let v = self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::ActivateGetUrl), vec![], req, RequestType::Json).await?.json::()?; + let v = WechatCommonResponse::parse::(v)?; + let url = v["url"].as_str().unwrap_or_default(); + Ok(url.to_string()) + } + + ///
+    /// 更新会员卡信息
+    /// 
+ pub async fn update_card_info(&self, req: MemberCardUpdateRequest) -> LabradorResult { + let v = self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::Update), vec![], req, RequestType::Json).await?.json::()?; + let v = WechatCommonResponse::parse::(v)?; + let send_check = v["send_check"].as_bool().unwrap_or_default(); + /// 此次更新是否需要提审,true为需要,false为不需要。 + Ok(send_check) + } + + ///
+    /// 解析跳转型开卡字段用户提交的资料.
+    /// 开发者在URL上截取ticket后须先进行urldecode
+    /// 
+ pub async fn get_activate_tempinfo(&self, activate_ticket: &str) -> LabradorResult { + let v = self.client.post(WechatMpMethod::MemberCard(MpMemeberCardMethod::Update), vec![], json!({ "activate_ticket": activate_ticket }), RequestType::Json).await?.json::()?; + WechatCommonResponse::parse::(v) + } +} + +//---------------------------------------------------------------------------------------------------------------------------- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpMemberCardCreateRequest { + pub card: MemberCardCreateRequest, +} + +impl WechatMpMemberCardCreateRequest { + pub fn valid_check(&self) -> LabradorResult<()> { + let req = &self.card; + if req.card_type.ne(MEMBER_CARD) { + return Err(LabraError::RequestError("卡券类型必须等于MEMBER_CARD".to_string())); + } + let member_card = &req.member_card; + if member_card.prerogative.is_empty() { + return Err(LabraError::RequestError("会员卡特权说明不能为空:prerogative".to_string())); + } + //卡片激活规则 + if !member_card.auto_activate && member_card.wx_activate && member_card.activate_url.is_none() { + return Err(LabraError::RequestError("会员卡激活方式为接口激活,activate_url不能为空".to_string())); + } + let base_info = &member_card.base_info; + if base_info.logo_url.is_empty() { + return Err(LabraError::RequestError("会员卡基本信息的商户logo:logo_url不能为空".to_string())); + } + if base_info.code_type.is_empty() { + return Err(LabraError::RequestError("会员卡基本信息的条码类型:code_type不能为空".to_string())); + } + if base_info.brand_name.is_empty() { + return Err(LabraError::RequestError("会员卡基本信息的商户名字:brand_name不能为空".to_string())); + } + if base_info.brand_name.len() > 12 { + return Err(LabraError::RequestError("会员卡基本信息的商户名字:brand_name长度不能大于12个汉字".to_string())); + } + if base_info.title.is_empty() { + return Err(LabraError::RequestError("会员卡基本信息的卡券名称:title不能为空".to_string())); + } + if base_info.title.len() > 9 { + return Err(LabraError::RequestError("会员卡基本信息的卡券名称:title长度不能大于9个汉字".to_string())); + } + if base_info.color.is_empty() { + return Err(LabraError::RequestError("会员卡基本信息的卡颜色:color不能为空".to_string())); + } + if CardColor::from_str(&base_info.color) == CardColor::Unknow { + return Err(LabraError::RequestError(format!("会员卡基本信息的卡颜色:{} 不支持", base_info.color))); + } + if base_info.notice.is_empty() { + return Err(LabraError::RequestError("会员卡基本信息的使用提醒:notice不能为空".to_string())); + } + if base_info.description.is_empty() { + return Err(LabraError::RequestError("会员卡基本信息的使用说明:description不能为空".to_string())); + } + + let date_info = &base_info.date_info; + let date_info_type = DateInfoType::from_str(&date_info.r#type); + if date_info_type == DateInfoType::Unknow { + return Err(LabraError::RequestError(format!("会员卡基本信息的使用日期类型:{} 不合法", date_info.r#type))); + } + //固定时长 + if date_info_type == DateInfoType::DATE_TYPE_FIX_TERM && (date_info.fixed_term.is_none() || date_info.fixed_begin_term.is_none()) { + return Err(LabraError::RequestError(format!("会员卡基本信息的使用日期为:固定日期 fixedTerm和fixedBeginTerm不能为空"))); + } + //固定期限 + if date_info_type == DateInfoType::DATE_TYPE_FIX_TIME_RANGE && (date_info.begin_timestamp.is_none() || date_info.end_timestamp.is_none()) { + return Err(LabraError::RequestError(format!("会员卡基本信息的使用日期为:固定期限 fixedTerm和fixedBeginTerm不能为空"))); + } + let current_tmp = get_timestamp(); + if date_info_type == DateInfoType::DATE_TYPE_FIX_TIME_RANGE && (date_info.begin_timestamp.unwrap_or_default() * 1000 < current_tmp || date_info.end_timestamp.unwrap_or_default() * 1000 < current_tmp || date_info.begin_timestamp.unwrap_or_default() > date_info.end_timestamp.unwrap_or_default()) { + return Err(LabraError::RequestError(format!("会员卡基本信息的使用日期为:固定期限,beginTimestamp和endTimestamp的值不合法,请检查"))); + } + + if !base_info.use_all_locations.unwrap_or_default() && base_info.location_id_list.is_none() { + return Err(LabraError::RequestError("会员卡基本信息的门店使用范围选择指定门店,门店列表:locationIdList不能为空".to_string())); + } + // 校验高级信息 + if let Some(advanced_info) = &member_card.advanced_info { + if let Some(busi_serv_list) = &advanced_info.business_service { + for bs in busi_serv_list { + if BusinessServiceType::from_str(bs) == BusinessServiceType::Unknow { + return Err(LabraError::RequestError(format!("会员卡高级信息的商户服务:{} 不合法", bs))); + } + } + } + } + Ok(()) + } +} + +/// 创建会员卡请求对象 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCardCreateRequest { + pub card_type: String, + pub member_card: MemberCard, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCard { + /// 会员卡背景图 + pub background_pic_url: String, + /// 基本信息 + pub base_info: BaseInfo, + /// 特权说明 + pub prerogative: String, + /// 自动激活 + pub auto_activate: bool, + /// 显示积分 + pub supply_bonus: bool, + /// 查看积分外链,设置跳转外链查看积分详情。仅适用于积分无法通过激活接口同步的情况下使用该字段. + pub bonus_url: Option, + /// 支持储值 + pub supply_balance: bool, + /// 余额外链,仅适用于余额无法通过激活接口同步的情况下使用该字段. + pub balance_url: Option, + /// 自定义会员类目1,会员卡激活后显示. + pub custom_field1: Option, + /// 自定义会员类目2 + pub custom_field2: Option, + /// 自定义会员类目3 + pub custom_field3: Option, + /// 积分清零规则 + pub bonus_cleared: Option, + /// 积分规则 + pub bonus_rules: Option, + /// 储值规则. + pub balance_rules: Option, + /// 激活会员卡的url. + pub activate_url: Option, + /// 激活会原卡url对应的小程序user_name,仅可跳转该公众号绑定的小程序. + pub activate_app_brand_user_name: Option, + /// 激活会原卡url对应的小程序path + pub activate_app_brand_pass: Option, + /// 自定义会员信息类目,会员卡激活后显示. + pub custom_cell1: Option, + /// 自定义会员信息类目,会员卡激活后显示. + pub custom_cell2: Option, + /// 自定义会员信息类目,会员卡激活后显示. + pub custom_cell3: Option, + /// 积分规则,JSON结构积分规则. + pub bonus_rule: Option, + /// 折扣,该会员卡享受的折扣优惠,填10就是九折. + pub discount: Option, + /// 创建优惠券特有的高级字段 + pub advanced_info: Option, + /// 是否支持一键激活 ,填true或false. + pub wx_activate: bool, + /// 是否支持跳转型一键激活,填true或false. + pub wx_activate_after_submit: bool, + /// 跳转型一键激活跳转的地址链接,请填写http:// 或者https://开头的链接. + pub wx_activate_after_submit_url: Option, + /// 参照https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V卡券内跳转小程序 + /// 积分信息类目对应的小程序 user_name,格式为原始id+@app + pub bonus_app_brand_user_name: Option, + /// 积分入口小程序的页面路径 + pub bonus_app_brand_pass: Option, + /// 余额信息类目对应的小程序 user_name,格式为原始id+@app + pub balance_app_brand_user_name: Option, + /// 余额入口小程序的页面路径 + pub balance_app_brand_pass: Option, +} + +/// 微信会员卡基本信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BaseInfo { + /// 卡券的商户logo,建议像素为300*300. + pub logo_url: String, + /// Code展示类型. + /// "CODE_TYPE_TEXT" 文本 "CODE_TYPE_BARCODE" 一维码 "CODE_TYPE_QRCODE" 二维码 "CODE_TYPE_ONLY_QRCODE" 仅显示二维码 "CODE_TYPE_ONLY_BARCODE" 仅显示一维码 "CODE_TYPE_NONE" 不显示任何码型 + pub code_type: String, + pub pay_info: Option, + /// 是否设置该会员卡中部的按钮同时支持微信支付刷卡和会员卡二维码 + pub is_pay_and_qrcode: Option, + /// 商户名字,字数上限为12个汉字. + pub brand_name: String, + /// 卡券名,字数上限为9个汉字 (建议涵盖卡券属性、服务及金额). + pub title: String, + /// 券颜色,按色彩规范标注填写Color010-Color100. + pub color: String, + /// 卡券使用提醒,字数上限为16个汉字. + pub notice: String, + /// 卡券使用说明,字数上限为1024个汉字. + pub description: String, + /// 商品信息 + pub sku: MemberCardSkuInfo, + /// 使用日期,有效期的信息. + pub date_info: DateInfo, + /// 是否自定义Code码,填写true或false. + /// 默认为false 通常自有优惠码系统的开发者选择自定义Code码,详情见 是否自定义code + pub use_custom_code: Option, + /// 是否指定用户领取,填写true或false。默认为false. + pub bind_openid: Option, + /// 客服电话 + pub service_phone: Option, + /// 门店位置ID,调用 POI门店管理接口 获取门店位置ID. + pub location_id_list: Option>, + /// 会员卡是否支持全部门店,填写后商户门店更新时会自动同步至卡券. + pub use_all_locations: Option, + /// 卡券中部居中的按钮,仅在卡券激活后且可用状态 时显示. + pub center_title: Option, + /// 显示在入口下方的提示语,仅在卡券激活后且可用状态时显示. + pub center_sub_title: Option, + /// 顶部居中的url,仅在卡券激活后且可用状态时显示. + pub center_url: Option, + /// 自定义跳转外链的入口名字 + pub custom_url_name: Option, + /// 自定义跳转的URL + pub custom_url: Option, + /// 显示在入口右侧的提示语 + pub custom_url_sub_title: Option, + /// 营销场景的自定义入口名称 + pub promotion_url_name: Option, + /// 入口跳转外链的地址链接 + pub promotion_url: Option, + /// 显示在营销入口右侧的提示语 + pub promotion_url_sub_title: Option, + /// 每人可领券的数量限制,建议会员卡每人限领一张. + pub get_limit: Option, + /// 每人可核销的数量限制,不填写默认为50. + pub use_limit: Option, + /// 卡券领取页面是否可分享,默认为true. + pub can_share: Option, + /// 卡券是否可转赠,默认为true. + pub can_give_friend: Option, + /// 用户点击进入会员卡时推送事件. + /// 填写true为用户点击进入会员卡时推送事件,默认为false。详情见 进入会员卡事件推送 + pub need_push_on_view: Option, + /// 微信小程序开放功能 小程序&卡券打通部分新增8个字段 https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1490190158&version=1&lang=zh_CN&platform=2 + /// 自定义使用入口跳转小程序的user_name,格式为原始id+@app + pub custom_app_brand_user_name: Option, + /// 自定义使用入口小程序页面地址 + pub custom_app_brand_pass: Option, + /// 小程序的user_name + pub center_app_brand_user_name: Option, + /// 自定义居中使用入口小程序页面地址 + pub center_app_brand_pass: Option, + /// 小程序的user_name + pub promotion_app_brand_user_name: Option, + /// 自定义营销入口小程序页面地址 + pub promotion_app_brand_pass: Option, + /// 小程序的user_name + pub activate_app_brand_user_name: Option, + /// 激活小程序页面地址 + pub activate_app_brand_pass: Option, + /// https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#2 + /// “CARD_STATUS_NOT_VERIFY”,待审核 ; + /// “CARD_STATUS_VERIFY_FAIL”,审核失败; + /// “CARD_STATUS_VERIFY_OK”,通过审核; + /// “CARD_STATUS_DELETE”,卡券被商户删除; + /// “CARD_STATUS_DISPATCH”,在公众平台投放过的卡券 + pub status: Option, +} +/// 微信会员卡高级字段信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AdvancedInfo { + /// 使用门槛(条件). + /// 若不填写使用条件则在券面拼写 :无最低消费限制,全场通用,不限品类;并在使用说明显示: 可与其他优惠共享 + pub use_condition: Option, + /// 封面摘要 + #[serde(rename="abstract")] + pub abstracts: Option, + /// 图文列表. + /// 显示在详情内页 ,优惠券券开发者须至少传入 一组图文列表 + pub text_image_list: Option>, + /// 商家服务类型. + /// 数组类型:BIZ_SERVICE_DELIVER 外卖服务; BIZ_SERVICE_FREE_PARK 停车位; BIZ_SERVICE_WITH_PET 可带宠物; BIZ_SERVICE_FREE_WIFI 免费wifi, 可多选 + pub business_service: Option>, + /// 使用时段限制 + pub time_limit: Option>, + /// 是否可以分享朋友 + pub share_friends: Option, +} +/// 使用时段限制 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TimeLimit { + /// 限制类型枚举值,支持填入 MONDAY 周一 TUESDAY 周二 WEDNESDAY 周三 THURSDAY 周四 FRIDAY 周五 SATURDAY 周六 SUNDAY 周日 此处只控制显示, 不控制实际使用逻辑,不填默认不显示 + #[serde(rename="type")] + pub r#type: Option, + /// 起始时间(小时),当前type类型下的起始时间(小时) ,如当前结构体内填写了MONDAY, 此处填写了10,则此处表示周一 10:00可用 + pub begin_hour: Option, + /// 起始时间(分钟),如当前结构体内填写了MONDAY, begin_hour填写10,此处填写了59, 则此处表示周一 10:59可用 + pub begin_minute: Option, + /// 结束时间(小时),如当前结构体内填写了MONDAY, 此处填写了20, 则此处表示周一 10:00-20:00可用 + pub end_hour: Option, + /// 结束时间(分钟),如当前结构体内填写了MONDAY, begin_hour填写10,此处填写了59, 则此处表示周一 10:59-00:59可用 + pub end_minute: Option, +} +/// 图文列表 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TextImageList { + /// 图片链接,必须调用 上传图片接口 上传图片获得链接,并在此填入, 否则报错 + pub image_url: Option, + /// 图文描述 + pub text: Option, +} +/// 封面摘要 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Abstracts { + /// 指定可用的商品类目,仅用于代金券类型 ,填入后将在券面拼写适用于xxx + #[serde(rename="abstract")] + pub abstracts: Option, + /// 封面图片列表. + /// 仅支持填入一 个封面图片链接, 上传图片接口 上传获取图片获得链接,填写 非CDN链接会报错,并在此填入。 建议图片尺寸像素850*350 + pub icon_url_list: Option, +} +/// 使用门槛 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UseCondition { + /// 指定可用的商品类目,仅用于代金券类型 ,填入后将在券面拼写适用于xxx + pub accept_category: Option, + /// 指定不可用的商品类目,仅用于代金券类型 ,填入后将在券面拼写不适用于xxxx + pub reject_category: Option, + /// 满减门槛字段,可用于兑换券和代金券 ,填入后将在全面拼写消费满xx元可用 + pub least_cost: Option, + /// 购买xx可用类型门槛,仅用于兑换 ,填入后自动拼写购买xxx可用 + pub object_use_for: Option, + /// 不可以与其他类型共享门槛,填写false时系统将在使用须知里 拼写“不可与其他优惠共享”, 填写true时系统将在使用须知里 拼写“可与其他优惠共享”, 默认为true + pub can_use_with_other_discount: Option, +} +/// 积分规则 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BonusRule { + /// 消费金额,以分为单位. + pub cost_money_unit: i64, + /// 对应增加的积分 + pub increase_bonus: i64, + /// 用户单次可获取的积分上限 + pub max_increase_bonus: i64, + /// 初始设置积分 + pub init_increase_bonus: i64, + /// 每使用积分 + pub cost_bonus_unit: i64, + /// 抵扣xx元,这里以分为单位). + pub reduce_money: i64, + /// 抵扣条件,满xx元(这里以分为单位)可用. + pub least_money_to_use_bonus: i64, + /// 抵扣条件,单笔最多使用xx积分. + pub max_reduce_bonus: i64, +} +/// 自定义会员信息类目 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CustomCell1 { + /// 入口名称 + pub name: String, + /// 入口右侧提示语,6个汉字内. + pub tips: String, + /// 点击类目跳转外链url + pub url: String, + /// 参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V卡券内跳转小程序参数说明:会员卡顶部的信息类目字段,包含以下两个字段 + /// 自定义信息类目小程序user_name,格式为原始id+@app + pub app_brand_user_name: Option, + /// 自定义信息类目小程序的页面路径 + pub app_brand_pass: Option, +} +/// 自定义会员信息类目 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CustomField { + /// 半自定义名称,当开发者变更这类类目信息的value值时 可以选择触发系统模板消息通知用户。 FIELD_NAME_TYPE_LEVEL 等级 FIELD_NAME_TYPE_COUPON 优惠券 FIELD_NAME_TYPE_STAMP 印花 FIELD_NAME_TYPE_DISCOUNT 折扣 FIELD_NAME_TYPE_ACHIEVEMEN 成就 FIELD_NAME_TYPE_MILEAGE 里程 FIELD_NAME_TYPE_SET_POINTS 集点 FIELD_NAME_TYPE_TIMS 次数 + pub name_type: String, + /// 自定义名称,当开发者变更这类类目信息的value值时 不会触发系统模板消息通知用户 + pub name: String, + /// 点击类目跳转外链url + pub url: String, + /// 参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1499332673_Unm7V卡券内跳转小程序参数说明:会员卡顶部的信息类目字段,包含以下两个字段 + /// 自定义信息类目小程序user_name,格式为原始id+@app + pub app_brand_user_name: Option, + /// 自定义信息类目小程序的页面路径 + pub app_brand_pass: Option, +} +/// 支付功能 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PayInfo { + /// 刷卡功能 + pub swipe_card: SwipeCard, +} +/// 刷卡功能 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SwipeCard { + /// 是否设置该会员卡支持拉出微信支付刷卡界面 + pub is_swipe_card: bool, +} +/// 商品信息 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCardSkuInfo { + /// 卡券库存的数量,不支持填写0,上限为100000000 + pub quantity: i32, + /// 卡券全部库存的数量,上限为100000000。 + /// https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#4 + pub total_quantity: i32, +} +/// 会员卡颜色 +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, PartialEq,Serialize, Deserialize)] +pub enum CardColor { + Color010, + Color020, + Color030, + Color040, + Color050, + Color060, + Color070, + Color080, + Color081, + Color082, + Color090, + Color100, + Color101, + Color102, + Unknow, +} + +impl CardColor { + fn from_str(color: &str) -> Self { + match color { + "#63b359" => Self::Color010, + "#2c9f67" => Self::Color020, + "#509fc9" => Self::Color030, + "#5885cf" => Self::Color040, + "#9062c0" => Self::Color050, + "#d09a45" => Self::Color060, + "#e4b138" => Self::Color070, + "#ee903c" => Self::Color080, + "#f08500" => Self::Color081, + "#a9d92d" => Self::Color082, + "#dd6549" => Self::Color090, + "#cc463d" => Self::Color100, + "#cf3e36" => Self::Color101, + "#5E6671" => Self::Color102, + _ => { + Self::Unknow + } + } + } +} +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, PartialEq,Serialize, Deserialize)] +pub enum DateInfoType { + /// 永久有效类型 + DATE_TYPE_PERMANENT, + /// 固定日期 + DATE_TYPE_FIX_TIME_RANGE, + /// 固定时长 + DATE_TYPE_FIX_TERM, + Unknow, +} + +impl DateInfoType { + fn from_str(v: &str) -> Self { + match v { + "DATE_TYPE_PERMANENT" => Self::DATE_TYPE_PERMANENT, + "DATE_TYPE_FIX_TIME_RANGE" => Self::DATE_TYPE_FIX_TIME_RANGE, + "DATE_TYPE_FIX_TERM" => Self::DATE_TYPE_FIX_TERM, + _ => { + Self::Unknow + } + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, PartialEq,Serialize, Deserialize)] +pub enum BusinessServiceType { + /// 外卖服务 + BIZ_SERVICE_DELIVER, + /// 停车位 + BIZ_SERVICE_FREE_PARK, + /// 可带宠物 + BIZ_SERVICE_WITH_PET, + /// WIFI + BIZ_SERVICE_FREE_WIFI, + Unknow, +} + +impl BusinessServiceType { + fn from_str(v: &str) -> Self { + match v { + "BIZ_SERVICE_DELIVER" => Self::BIZ_SERVICE_DELIVER, + "BIZ_SERVICE_FREE_PARK" => Self::BIZ_SERVICE_FREE_PARK, + "BIZ_SERVICE_WITH_PET" => Self::BIZ_SERVICE_WITH_PET, + "BIZ_SERVICE_FREE_WIFI" => Self::BIZ_SERVICE_FREE_WIFI, + _ => { + Self::Unknow + } + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, PartialEq,Serialize, Deserialize)] +pub enum CardRichFieldType { + /// 自定义单选 + FORM_FIELD_RADIO, + /// 自定义选择项 + FORM_FIELD_SELECT, + /// 自定义多选 + FORM_FIELD_CHECK_BOX, + Unknow, +} + +#[allow(unused)] +impl CardRichFieldType { + fn from_str(v: &str) -> Self { + match v { + "FORM_FIELD_RADIO" => Self::FORM_FIELD_RADIO, + "FORM_FIELD_SELECT" => Self::FORM_FIELD_SELECT, + "FORM_FIELD_CHECK_BOX" => Self::FORM_FIELD_CHECK_BOX, + _ => { + Self::Unknow + } + } + } +} + +/// 使用日期,有效期的信息. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DateInfo { + /// 使用时间的类型. + /// 支持固定时长有效类型 固定日期有效类型 永久有效类型:DATE_TYPE_FIX_TERM_RANGE、DATE_TYPE_FIX_TERM 、DATE_TYPE_PERMANENT + #[serde(rename="type")] + pub r#type: String, + /// 起用时间. + /// type为DATE_TYPE_FIX_TIME_RANGE时专用, 表示起用时间。从1970年1月1日00:00:00至起用时间的秒数 ( 东八区时间,UTC+8,单位为秒 ) + pub begin_timestamp: Option, + /// 结束时间. + /// type为DATE_TYPE_FIX_TERM_RANGE时专用,表示结束时间 ( 东八区时间,UTC+8,单位为秒 ) + pub end_timestamp: Option, + /// 自领取后多少天开始生效. + /// type为DATE_TYPE_FIX_TERM时专用,表示自领取后多少天开始生效。(单位为天) + pub fixed_term: Option, + /// 自领取后多少天开始生效. + /// type为DATE_TYPE_FIX_TERM时专用,表示自领取后多少天开始生效。(单位为天) + pub fixed_begin_term: Option, +} + +/// 使用日期,有效期的信息. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpCardCreateResponse { + pub card_id: String, +} + +/// 会员卡激活接口的参数 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpMemberCardActivateRequest { + /// 会员卡编号,由开发者填入,作为序列号显示在用户的卡包里。可与Code码保持等值。 + pub membership_number: String, + /// 领取会员卡用户获得的code + pub code: String, + /// 卡券ID,自定义code卡券必填 + pub card_id: Option, + /// 商家自定义会员卡背景图,须先调用上传图片接口将背景图上传至CDN,否则报错。卡面设计请遵循微信会员卡自定义背景设计规范 + pub background_pic_url: Option, + /// 激活后的有效起始时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。 + pub activate_begin_time: Option, + /// 激活后的有效截至时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。 + pub activate_end_time: Option, + /// 初始积分,不填为0。 + pub init_bonus: Option, + /// 积分同步说明 + pub init_bonus_record: Option, + /// 初始余额,不填为0。 + pub init_balance: Option, + /// 创建时字段custom_field1定义类型的初始值,限制为4个汉字,12字节。 + pub init_custom_field_value1: Option, + /// 创建时字段custom_field2定义类型的初始值,限制为4个汉字,12字节。 + pub init_custom_field_value2: Option, + /// 创建时字段custom_field3定义类型的初始值,限制为4个汉字,12字节。 + pub init_custom_field_value3: Option, +} + +/// 拉取会员信息返回的结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpMemberCardUserInfoResponse { + /// 用户在本公众号内唯一识别码 + pub openid: String, + /// 用户昵称 + pub nickname: Option, + /// 积分信息 + pub bonus: Option, + /// 余额信息 + pub balance: Option, + /// 用户性别 + pub sex: Option, + /// 会员信息 + pub user_info: Option, + /// 当前用户会员卡状态,NORMAL 正常 EXPIRE 已过期 GIFTING 转赠中 GIFT_SUCC 转赠成功 GIFT_TIMEOUT 转赠超时 DELETE 已删除,UNAVAILABLE 已失效 + pub user_card_status: Option, + pub has_active: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCardUserInfo { + pub custom_field_list: Vec, + pub common_field_list: Vec, +} +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NameValues { + pub name: Option, + pub value: Option, + pub value_list: Option>, +} + + + +///
+/// 更新会员信息所需字段消息。
+///
+/// 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时
+/// add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。
+/// 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。
+/// 
+#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpMemberCardUpdateRequest { + /// 领取会员卡用户获得的code + pub code: String, + /// 卡券ID,自定义code卡券必填 + pub card_id: Option, + /// 支持商家激活时针对单个会员卡分配自定义的会员卡背景 + pub background_pic_url: Option, + /// 需要设置的积分全量值,传入的数值会直接显示 + pub bonus: Option, + /// 本次积分变动值,传负数代表减少 + pub add_bonus: Option, + /// 商家自定义积分消耗记录,不超过14个汉字 + pub record_bonus: Option, + /// 需要设置的余额全量值,传入的数值会直接显示在卡面 + pub balance: Option, + /// 本次余额变动值,传负数代表减少 + pub add_balance: Option, + /// 商家自定义金额消耗记录,不超过14个汉字。 + pub record_balance: Option, + pub notify_optional: Option, + /// 创建时字段custom_field1定义类型的最新数值,限制为4个汉字,12字节。 + pub custom_field_value1: Option, + /// 创建时字段custom_field2定义类型的最新数值,限制为4个汉字,12字节。 + pub custom_field_value2: Option, + /// 创建时字段custom_field3定义类型的最新数值,限制为4个汉字,12字节。 + pub custom_field_value3: Option, +} + +/// 控制原生消息结构体,包含各字段的消息控制字段。 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NotifyOptional { + /// 积分变动时是否触发系统模板消息,默认为true + pub is_notify_bonus: Option, + /// 余额变动时是否触发系统模板消息,默认为true + pub is_notify_balance: Option, + /// 自定义group1变动时是否触发系统模板消息,默认为false。(2、3同理) + pub is_notify_custom_field1: Option, + pub is_notify_custom_field2: Option, + pub is_notify_custom_field3: Option, +} + + +/// 更新会员信息的接口调用后的返回结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpMemberCardUpdateResponse { + /// 用户在本公众号内唯一识别码 + pub openid: String, + pub result_bonus: Option, + /// 余额信息 + pub result_balance: Option, +} + +/// 会员卡激活,用户字段提交请求 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpMemberCardActivateUserFormRequest { + /// 用户在本公众号内唯一识别码 + pub card_id: String, + pub service_statement: Value, + pub bind_old_card: Value, + /// 必填项 + pub required_form: MemberCardUserForm, + /// 可选项 + pub optional_form: MemberCardUserForm, +} + +impl WechatMpMemberCardActivateUserFormRequest { + /// 绑定老会员卡信息 + pub fn set_bind_old_card(&mut self, name: &str, url: &str) { + if name.is_empty() || url.is_empty() { + return; + } + self.bind_old_card["name"] = name.into(); + self.bind_old_card["url"] = url.into(); + } + /// 设置服务声明,用于放置商户会员卡守则 + pub fn set_service_statement(&mut self, name: &str, url: &str) { + if name.is_empty() || url.is_empty() { + return; + } + self.service_statement["name"] = name.into(); + self.service_statement["url"] = url.into(); + } +} + +/// 用户表单对象 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCardUserForm { + + /// 富文本类型字段列表 + pub rich_field_list: Vec, + /// 文本选项类型列表 + pub custom_field_list: Vec, + /// 微信格式化的选项类型 + pub common_field_id_list: Vec, +} + +/// 富文本字段 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCardUserFormRichField { + /// 富文本类型 + #[serde(rename="type")] + pub r#type: String, + pub name: String, + pub values: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ActivatePluginParam { + pub encrypt_card_id: String, + pub outer_str: String, + pub biz: String, +} + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCardUpdateRequest { + pub card_id: String, + pub member_card: MemberCardUpdate, +} + +/// 会员卡更新对象 +/// 以下字段顺序根据微信官方文档顺序相同,不能传入非文档之外的字段 +/// https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1466494654_K9rNz +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MemberCardUpdate { + /// 基本信息 + pub base_info: BaseInfoUpdate, + /// 会员卡背景图 + pub background_pic_url: String, + /// 是否支持积分,仅支持从false变为true,默认为false + pub supply_bonus: bool, + /// 积分清零规则 + pub bonus_cleared: Option, + /// 积分规则 + pub bonus_rules: Option, + /// 查看积分外链,设置跳转外链查看积分详情。仅适用于积分无法通过激活接口同步的情况下使用该字段. + pub bonus_url: Option, + /// 余额外链,仅适用于余额无法通过激活接口同步的情况下使用该字段. + pub balance_url: Option, + /// 支持储值 + pub supply_balance: bool, + /// 储值规则. + pub balance_rules: Option, + /// 特权说明 + pub prerogative: String, + /// 自动激活 + pub auto_activate: bool, + /// 是否支持一键激活 ,填true或false. + pub wx_activate: bool, + /// 激活会员卡的url. + pub activate_url: Option, + /// 自定义会员类目1,会员卡激活后显示. + pub custom_field1: Option, + /// 自定义会员类目2 + pub custom_field2: Option, + /// 自定义会员类目3 + pub custom_field3: Option, + /// 自定义会员信息类目,会员卡激活后显示. + pub custom_cell1: Option, + /// 自定义会员信息类目,会员卡激活后显示. + pub custom_cell2: Option, + /// 自定义会员信息类目,会员卡激活后显示. + pub custom_cell3: Option, + /// 积分规则,JSON结构积分规则. + pub bonus_rule: Option, + /// 折扣,该会员卡享受的折扣优惠,填10就是九折. + pub discount: Option, +} + +/// 微信会员卡基本信息更新 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BaseInfoUpdate { + /// 卡券名,字数上限为9个汉字 (建议涵盖卡券属性、服务及金额). + pub title: String, + /// 卡券的商户logo,建议像素为300*300. + pub logo_url: String, + /// 卡券使用提醒,字数上限为16个汉字. + pub notice: String, + /// 卡券使用说明,字数上限为1024个汉字. + pub description: String, + /// 客服电话 + pub service_phone: Option, + /// 券颜色,按色彩规范标注填写Color010-Color100. + pub color: String, + /// 门店位置ID,调用 POI门店管理接口 获取门店位置ID. + pub location_id_list: Option>, + /// 会员卡是否支持全部门店,填写后商户门店更新时会自动同步至卡券. + pub use_all_locations: Option, + /// 卡券中部居中的按钮,仅在卡券激活后且可用状态 时显示. + pub center_title: Option, + /// 显示在入口下方的提示语,仅在卡券激活后且可用状态时显示. + pub center_sub_title: Option, + /// 顶部居中的url,仅在卡券激活后且可用状态时显示. + pub center_url: Option, + /// 自定义跳转外链的入口名字 + pub custom_url_name: Option, + /// 自定义跳转的URL + pub custom_url: Option, + /// 显示在入口右侧的提示语 + pub custom_url_sub_title: Option, + /// 营销场景的自定义入口名称 + pub promotion_url_name: Option, + /// 入口跳转外链的地址链接 + pub promotion_url: Option, + /// 显示在营销入口右侧的提示语 + pub promotion_url_sub_title: Option, + /// Code展示类型. + /// "CODE_TYPE_TEXT" 文本 "CODE_TYPE_BARCODE" 一维码 "CODE_TYPE_QRCODE" 二维码 "CODE_TYPE_ONLY_QRCODE" 仅显示二维码 "CODE_TYPE_ONLY_BARCODE" 仅显示一维码 "CODE_TYPE_NONE" 不显示任何码型 + pub code_type: String, + pub pay_info: Option, + /// 是否设置该会员卡中部的按钮同时支持微信支付刷卡和会员卡二维码 + pub is_pay_and_qrcode: Option, + /// 每人可领券的数量限制,建议会员卡每人限领一张. + pub get_limit: Option, + /// 卡券领取页面是否可分享,默认为true. + pub can_share: Option, + /// 卡券是否可转赠,默认为true. + pub can_give_friend: Option, + /// 使用日期,有效期的信息. + pub date_info: DateInfo, + /// 微信小程序开放功能 小程序&卡券打通部分新增8个字段 https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1490190158&version=1&lang=zh_CN&platform=2 + /// 自定义使用入口跳转小程序的user_name,格式为原始id+@app + pub custom_app_brand_user_name: Option, + /// 自定义使用入口小程序页面地址 + pub custom_app_brand_pass: Option, + /// 小程序的user_name + pub center_app_brand_user_name: Option, + /// 自定义居中使用入口小程序页面地址 + pub center_app_brand_pass: Option, + /// 小程序的user_name + pub promotion_app_brand_user_name: Option, + /// 自定义营销入口小程序页面地址 + pub promotion_app_brand_pass: Option, + /// 小程序的user_name + pub activate_app_brand_user_name: Option, + /// 激活小程序页面地址 + pub activate_app_brand_pass: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WechatMpMemberCardActivateTempInfoResponse { + pub user_info: MemberCardUserInfo, +} \ No newline at end of file diff --git a/src/wechat/mp/api/mod.rs b/src/wechat/mp/api/mod.rs index 6712175..be8f0ba 100644 --- a/src/wechat/mp/api/mod.rs +++ b/src/wechat/mp/api/mod.rs @@ -8,6 +8,8 @@ mod template_msg; mod subscribe_msg; mod wifi; mod ocr; +mod member; +mod card; pub use self::oauth2::*; pub use self::qrcode::*; @@ -19,5 +21,7 @@ pub use self::media::*; pub use self::menu::*; pub use self::wifi::*; pub use self::ocr::*; +pub use self::member::*; +pub use self::card::*; diff --git a/src/wechat/mp/api/wifi.rs b/src/wechat/mp/api/wifi.rs index 6be6d8c..967f6bc 100644 --- a/src/wechat/mp/api/wifi.rs +++ b/src/wechat/mp/api/wifi.rs @@ -76,7 +76,7 @@ impl<'a, T: SessionStore> WeChatMpWifi<'a, T> { //---------------------------------------------------------------------------------------------------------------------------- -#[derive(Debug, Clone, PartialEq, Eq,Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct WechatMpWifiShopDataResponse { /// 门店名称 pub shop_name: Option, @@ -106,7 +106,7 @@ pub struct WechatMpWifiShopDataResponse { pub poi_id: Option, } -#[derive(Debug, Clone, PartialEq, Eq,Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SsidPassword { /// 无线网络设备的ssid pub ssid: Option, diff --git a/src/wechat/mp/constants.rs b/src/wechat/mp/constants.rs index 03ca84f..fb342c3 100644 --- a/src/wechat/mp/constants.rs +++ b/src/wechat/mp/constants.rs @@ -18,6 +18,7 @@ pub static REFRESH_TOKEN: &str = "refresh_token"; pub static QR_SCENE: &str = "QR_SCENE"; +pub static QR_CODE: &str = "QR_CODE"; pub static QR_LIMIT_SCENE: &str = "QR_LIMIT_SCENE"; pub static IMG_URL: &str = "img_url"; @@ -26,5 +27,6 @@ pub static TICKET_TYPE_JSAPI: &str = "jsapi"; pub static TICKET_TYPE: &str = "type"; pub static TICKET_TYPE_SDK: &str = "2"; pub static TICKET_TYPE_WXCARD: &str = "wx_card"; +pub static MEMBER_CARD: &str = "MEMBER_CARD"; diff --git a/src/wechat/mp/method.rs b/src/wechat/mp/method.rs index 39d37ce..1127bfc 100644 --- a/src/wechat/mp/method.rs +++ b/src/wechat/mp/method.rs @@ -22,6 +22,10 @@ pub enum WechatMpMethod { Ocr(MpOcrMethod), /// wifi服务 Wifi(MpWifiMethod), + /// 会员卡服务 + MemberCard(MpMemeberCardMethod), + /// 卡券服务 + Card(MpCardMethod), /// 用户服务 User(MpUserMethod), /// 菜单服务 @@ -182,6 +186,130 @@ impl MpWifiMethod { +#[allow(unused)] +#[derive(Debug, PartialEq, Clone)] +pub enum MpMemeberCardMethod { + Create, + Activate, + GetUserInfo, + UpdateUser, + /// 会员卡激活之微信开卡接口(wx_activate=true情况调用). + ActivateSetUser, + /// 获取会员卡开卡插件参数 + ActivateGetUrl, + /// 会员卡信息更新 + Update, + /// 跳转型会员卡开卡字段. + /// 获取用户提交资料(wx_activate=true情况调用),开发者根据activate_ticket获取到用户填写的信息 + GetActivateTempInfo, +} + +#[allow(unused)] +impl MpMemeberCardMethod { + pub fn get_method(&self) -> String { + match *self { + MpMemeberCardMethod::Create => String::from("/card/create"), + MpMemeberCardMethod::Activate => String::from("/card/membercard/activate"), + MpMemeberCardMethod::GetUserInfo => String::from("/card/membercard/userinfo/get"), + MpMemeberCardMethod::UpdateUser => String::from("/card/membercard/updateuser"), + MpMemeberCardMethod::ActivateSetUser => String::from("/card/membercard/activateuserform/set"), + MpMemeberCardMethod::ActivateSetUser => String::from("/card/membercard/activateuserform/set"), + MpMemeberCardMethod::ActivateGetUrl => String::from("/card/membercard/activate/geturl"), + MpMemeberCardMethod::Update => String::from("/card/update"), + MpMemeberCardMethod::GetActivateTempInfo => String::from("/card/membercard/activatetempinfo/get"), + } + } +} + + + + + +#[allow(unused)] +#[derive(Debug, PartialEq, Clone)] +pub enum MpCardMethod { + Create, + Get, + Update, + CodeDecrypt, + CodeGet, + CodeMark, + SetWhiteList, + CreateQrcode, + CodeConsume, + CreateLandingpage, + /// 将用户的卡券设置为失效状态 + UnavailabeCode, + Delete, + /// 导入code接口 + CodeDeposit, + /// 查询导入code数目接口 + GetDepositCount, + /// 核查code接口 + CheckCode, + /// 图文消息群发卡券 + GetHtml, + /// 修改库存接口 + ModifyStock, + /// 更改Code接口 + UpdateCode, + /// 设置买单接口 + SetPayCell, + /// 设置自助核销接口 + SetSelfConsumerCell, + /// 获取用户已领取卡券接口 + GetUserCardList, + /// 创建子商户 + SubmitSubmerchant, + /// 卡券开放类目查询接口 + GetApplyProtocol, + /// 修改子商户 + UpdateSubmerchant, + /// 拉取单个子商户信息接口 + GetSubmerchant, + /// 批量拉取子商户信息接口 + BatchGetSubmerchant, +} + +#[allow(unused)] +impl MpCardMethod { + pub fn get_method(&self) -> String { + match *self { + MpCardMethod::Create => String::from("/card/create"), + MpCardMethod::Get => String::from("/card/get"), + MpCardMethod::Update => String::from("/card/update"), + MpCardMethod::CodeDecrypt => String::from("/card/code/decrypt"), + MpCardMethod::CodeGet => String::from("/card/code/get"), + MpCardMethod::CodeConsume => String::from("/card/code/consume"), + MpCardMethod::CodeMark => String::from("/card/code/mark"), + MpCardMethod::SetWhiteList => String::from("/card/testwhitelist/set"), + MpCardMethod::CreateQrcode => String::from("/card/qrcode/create"), + MpCardMethod::CreateLandingpage => String::from("/card/landingpage/create"), + MpCardMethod::UnavailabeCode => String::from("/card/code/unavailable"), + MpCardMethod::Delete => String::from("/card/delete"), + MpCardMethod::CodeDeposit => String::from("/card/code/deposit"), + MpCardMethod::GetDepositCount => String::from("/card/code/getdepositcount"), + MpCardMethod::CheckCode => String::from("/card/code/checkcode"), + MpCardMethod::GetHtml => String::from("/card/mpnews/gethtml"), + MpCardMethod::ModifyStock => String::from("/card/modifystock"), + MpCardMethod::UpdateCode => String::from("/card/code/update"), + MpCardMethod::SetPayCell => String::from("/card/paycell/set"), + MpCardMethod::SetSelfConsumerCell => String::from("/card/selfconsumecell/set"), + MpCardMethod::GetUserCardList => String::from("/card/user/getcardlist"), + MpCardMethod::SubmitSubmerchant => String::from("/card/submerchant/submit"), + MpCardMethod::UpdateSubmerchant => String::from("/card/submerchant/update"), + MpCardMethod::GetApplyProtocol => String::from("/card/getapplyprotocol"), + MpCardMethod::GetSubmerchant => String::from("/card/submerchant/get"), + MpCardMethod::BatchGetSubmerchant => String::from("/card/submerchant/batchget"), + MpCardMethod::BatchGetSubmerchant => String::from("/card/submerchant/batchget"), + } + } +} + + + + + #[allow(unused)] #[derive(Debug, PartialEq, Clone)] @@ -267,13 +395,15 @@ impl RequestMethod for WechatMpMethod { WechatMpMethod::CustomService(v) => v.get_method(), WechatMpMethod::User(v) => v.get_method(), WechatMpMethod::Menu(v) => v.get_method(), + WechatMpMethod::MemberCard(v) => v.get_method(), WechatMpMethod::Wifi(v) => v.get_method(), WechatMpMethod::TemplateMessage(v) => v.get_method(), WechatMpMethod::QrCode(v) => v.get_method(), WechatMpMethod::Media(v) => v.get_method(), WechatMpMethod::Custom(v) => v.to_string(), WechatMpMethod::SubscribeMessage(v) => v.get_method(), - WechatMpMethod::Ocr(v) => v.get_method() + WechatMpMethod::Ocr(v) => v.get_method(), + WechatMpMethod::Card(v) => v.get_method(), } } }