alipay_global/
models.rs

1use super::errors::Error;
2use base64::Engine;
3use chrono::{DateTime, Utc};
4use juniper::{GraphQLEnum, GraphQLObject};
5use rsa::{
6    pkcs1::{DecodeRsaPrivateKey, Error as Pkcs1Error},
7    pkcs8::{spki::Error as Pkcs8Error, DecodePublicKey},
8    Hash, PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey,
9};
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::fs::read_to_string;
13use std::path::PathBuf;
14use std::string::ToString;
15use strum_macros::Display;
16// use std::io::{Error as StdError, Result, ErrorKind};
17
18pub enum AlipayAction {
19    PAY,
20    REFUND,
21    INQUIRY,
22}
23
24impl ToString for AlipayAction {
25    fn to_string(&self) -> String {
26        match self {
27            AlipayAction::PAY => String::from("pay"),
28            AlipayAction::REFUND => String::from("refund"),
29            AlipayAction::INQUIRY => String::from("inquiryPayment"),
30        }
31    }
32}
33
34/// Alipay Client Info and Secret
35pub struct AlipayClientSecret {
36    pub action: AlipayAction,
37    pub client_id: String,
38    pub sandbox: bool,
39    pub private_key_pem: Option<String>,
40    pub private_key_pem_file: Option<Box<PathBuf>>,
41    pub alipay_public_key_pem: Option<String>,
42    pub alipay_public_key_pem_file: Option<Box<PathBuf>>,
43}
44
45impl HasPrivateKey for AlipayClientSecret {
46    fn get_private_key(&self) -> Result<RsaPrivateKey, Pkcs1Error> {
47        let key: String;
48        if (self.private_key_pem_file.is_some()) {
49            let s = read_to_string(self.private_key_pem_file.clone().unwrap().as_path()).unwrap();
50            key = s;
51        } else {
52            key = format_pkcs1_private_key(&self.private_key_pem.clone().unwrap());
53        }
54
55        // get private obj
56        load_private_key(&key)
57    }
58}
59
60impl HasPublicKey for AlipayClientSecret {
61    fn get_public_key(&self) -> Result<RsaPublicKey, Pkcs8Error> {
62        let key: String;
63        if (self.alipay_public_key_pem_file.is_some()) {
64            let s =
65                read_to_string(self.alipay_public_key_pem_file.clone().unwrap().as_path()).unwrap();
66            key = s;
67        } else {
68            key = format_pem_public_key(&self.alipay_public_key_pem.clone().unwrap());
69        }
70
71        // get private obj
72        load_public_key(&key)
73    }
74}
75
76pub fn load_private_key(private_key_str: &str) -> Result<RsaPrivateKey, Pkcs1Error> {
77    RsaPrivateKey::from_pkcs1_pem(&private_key_str)
78}
79
80pub fn load_public_key(public_key_str: &str) -> Result<RsaPublicKey, Pkcs8Error> {
81    RsaPublicKey::from_public_key_pem(&public_key_str)
82}
83
84const PUBLIC_KEY_PREFIX: &str = "-----BEGIN PUBLIC KEY-----";
85const PUBLIC_KEY_SUFFIX: &str = "-----END PUBLIC KEY-----";
86
87const PKCS1_PREFIX: &str = "-----BEGIN RSA PRIVATE KEY-----";
88const PKCS1_SUFFIX: &str = "-----END RSA PRIVATE KEY-----";
89
90const PKCS8_PREFIX: &str = "-----BEGIN PRIVATE KEY-----";
91const PKCS8_SUFFIX: &str = "-----END PRIVATE KEY-----";
92
93pub fn format_pkcs1_private_key(raw: &str) -> String {
94    format_key(raw, PKCS1_PREFIX, PKCS1_SUFFIX, 64)
95}
96
97pub fn format_pkcs8_private_key(raw: &str) -> String {
98    format_key(raw, PKCS8_PREFIX, PKCS8_SUFFIX, 64)
99}
100
101pub fn format_pem_public_key(raw: &str) -> String {
102    format_key(raw, PUBLIC_KEY_PREFIX, PUBLIC_KEY_SUFFIX, 64)
103}
104
105fn format_key(raw: &str, prefix: &str, suffix: &str, line_count: usize) -> String {
106    let mut buffer = Vec::new();
107    buffer.append(prefix.as_bytes().to_vec().as_mut());
108    buffer.append("\n".as_bytes().to_vec().as_mut());
109    let raw_len = line_count;
110    let key_len = raw.len();
111    let mut raws = key_len / raw_len;
112    let temp = key_len % raw_len;
113    if temp > 0 {
114        raws += 1;
115    }
116    let mut start = 0;
117    let mut end = start + raw_len;
118    for i in 0..raws {
119        if i == raws - 1 {
120            buffer.append(raw.get(start..).unwrap().as_bytes().to_vec().as_mut());
121        } else {
122            buffer.append(raw.get(start..end).unwrap().as_bytes().to_vec().as_mut());
123        }
124        buffer.append("\n".as_bytes().to_vec().as_mut());
125        start += raw_len;
126        end = start + raw_len
127    }
128    buffer.append(suffix.as_bytes().to_vec().as_mut());
129    buffer.append("\n".as_bytes().to_vec().as_mut());
130    String::from_utf8(buffer.clone()).unwrap()
131}
132
133/// Minimum Information to initialize a payment cashier
134#[derive(Serialize)]
135pub struct CashierPaymentSimple {
136    pub payment_request_id: String,
137    pub currency: String,
138    pub amount: i32,
139    pub redict_url: String,
140    pub notifiy_url: String,
141    pub order_description: String,
142    pub reference_order_id: Option<String>,
143    pub terminal_type: Option<TerminalType>,
144}
145
146/// A Trait contains all data for alipay signing
147pub trait Signable {
148    fn get_value(&self) -> Value;
149}
150
151/// A Trait contains private key data
152pub trait HasPrivateKey {
153    fn get_private_key(&self) -> Result<RsaPrivateKey, Pkcs1Error>;
154}
155
156pub trait HasPublicKey {
157    fn get_public_key(&self) -> Result<RsaPublicKey, Pkcs8Error>;
158}
159
160/// Payment Cashier Request Object
161/// see: https://global.alipay.com/docs/ac/ams/payment_cashier
162///
163/// skip attributes
164/// - paymentFactor
165/// - paymentExpiryTime
166/// - userRegion
167/// - creditPayPlan
168/// - appId
169/// - merchantRegion
170#[derive(Serialize)]
171#[serde(rename_all = "camelCase")]
172pub struct CashierPaymentFull {
173    /// Represents the payment product that is being used, which is stipulated in the contract. For Cashier Payment, the value is fixed as CASHIER_PAYMENT.
174    pub product_code: String,
175    /// The unique ID assigned by a merchant to identify a payment request. Alipay uses this field for idempotence control.
176    /// More information about this field:
177    /// This field is an API idempotency field. For payment requests that are initiated with the same value of paymentRequestId and reach a final status of S or F, the same result is to be returned for the request.
178    /// Maximum length: 64 characters
179    pub payment_request_id: String,
180    pub order: Order,
181    pub payment_amount: Amount,
182    pub payment_method: PaymentMethod,
183    pub payment_redirect_url: String,
184    pub payment_notify_url: String,
185    pub settlement_strategy: SettlementStrategy,
186    pub env: Env,
187}
188
189impl CashierPaymentFull {
190    pub fn to_string(&self) -> String {
191        serde_json::to_value(self).unwrap().to_string()
192    }
193}
194
195impl From<&CashierPaymentSimple> for CashierPaymentFull {
196    fn from(value: &CashierPaymentSimple) -> Self {
197        let CashierPaymentSimple {
198            payment_request_id,
199            redict_url,
200            notifiy_url,
201            ..
202        } = value;
203        let order = Order::from(value);
204        let payment_amount = Amount::from(value);
205        let payment_method = PaymentMethod::from(value);
206        let settlement_strategy = SettlementStrategy::from(value);
207        let env = Env::from(value);
208        Self {
209            product_code: String::from("CASHIER_PAYMENT"),
210            payment_request_id: payment_request_id.clone(),
211            order,
212            payment_amount,
213            payment_method,
214            payment_redirect_url: redict_url.clone(),
215            payment_notify_url: notifiy_url.clone(),
216            settlement_strategy,
217            env,
218        }
219    }
220}
221
222impl Signable for CashierPaymentFull {
223    fn get_value(&self) -> Value {
224        serde_json::to_value(self).unwrap()
225    }
226}
227
228pub struct RequestEnv {
229    pub path: String,
230    pub domain: String,
231}
232impl From<&AlipayClientSecret> for RequestEnv {
233    fn from(value: &AlipayClientSecret) -> Self {
234        if value.sandbox {
235            Self {
236                path: String::from(format!(
237                    "/ams/sandbox/api/v1/payments/{}",
238                    value.action.to_string()
239                )),
240                domain: String::from("https://open-global.alipay.com"),
241            }
242        } else {
243            Self {
244                path: String::from(format!("/ams/api/v1/payments/{}", value.action.to_string())),
245                domain: String::from("https://open-global.alipay.com"),
246            }
247        }
248    }
249}
250
251impl RequestEnv {
252    pub fn get_request_url(&self) -> String {
253        self.domain.clone() + self.path.as_str()
254    }
255}
256
257/// Information about the environment where the order is placed, such as the device information.
258///
259/// skip attributes
260/// - osType
261/// - browserInfo
262/// - colorDepth
263/// - screenHeight
264/// - screenWidth
265/// - timeZoneOffset
266/// - deviceBrand
267/// - deviceModel
268/// - deviceTokenId
269/// - clientIp
270/// - deviceLanguage
271/// - deviceId
272/// - extendInfo
273#[derive(Serialize)]
274#[serde(rename_all = "camelCase")]
275pub struct Env {
276    /// Terminal type of which the merchant service applies to. Valid values are:
277    /// WEB: The client-side terminal type is a website, which is opened via a PC browser.
278    /// WAP: The client-side terminal type is an H5 page, which is opened via a mobile browser.
279    /// APP: The client-side terminal type is a mobile application.
280    /// MINI_APP: The terminal type of the merchant side is a mini program on the mobile phone.  
281    terminal_type: TerminalType,
282}
283
284impl From<&CashierPaymentSimple> for Env {
285    fn from(value: &CashierPaymentSimple) -> Self {
286        let CashierPaymentSimple { terminal_type, .. } = value;
287        let tt = terminal_type.clone().unwrap_or(TerminalType::WEB);
288        Self { terminal_type: tt }
289    }
290}
291
292#[derive(Serialize, Clone, Copy, PartialEq, Eq)]
293pub enum TerminalType {
294    WEB,
295    WAP,
296    APP,
297    MINI_APP,
298}
299
300/// The order amount of the merchant that directly provides services or goods to the customer. This field is used for user consumption records display or payment results page.
301#[derive(Serialize, Deserialize, Debug, Clone)]
302#[serde(rename_all = "camelCase")]
303pub struct Amount {
304    /// The transaction currency that is specified in the contract. A 3-letter currency code that follows the ISO 4217 standard.
305    /// More information about this field:
306    /// Maximum length: 3 characters
307    pub currency: String,
308    /// The amount to charge as a positive integer in the smallest currency unit. (That is, 100 cents to charge $1.00, or 100 to charge JPY 100, a 0-decimal currency).
309    /// Note: For details about the smallest currency unit, see Smallest unit of the currency.
310    /// More information about this field:
311    /// Value range: 1 - unlimited
312    ///
313    /// value can be i32 or String when you are sending request
314    /// HOWEVER, value can only be String when deserializing from response body
315    value: String,
316}
317impl Amount {
318    pub fn value(&self) -> u32 {
319        self.value.parse().unwrap()
320    }
321    pub fn currency(&self) -> String {
322        self.currency.clone()
323    }
324}
325impl From<&CashierPaymentSimple> for Amount {
326    fn from(value: &CashierPaymentSimple) -> Self {
327        let CashierPaymentSimple {
328            currency, amount, ..
329        } = value;
330        Self {
331            value: amount.to_string().clone(),
332            currency: currency.clone(),
333        }
334    }
335}
336
337/// The payment method that is used to collect the payment by the merchant or acquirer.
338///
339/// skip attributes
340/// - paymentMethodId
341/// - paymentMethodMetaData
342/// - customerId
343/// - extendInfo
344#[derive(Serialize)]
345#[serde(rename_all = "camelCase")]
346pub struct PaymentMethod {
347    /// The payment method type that is included in payment method options. By specifying the value of this parameter, you can receive the cashier URL of the specified payment method returned by Alipay. See Payment methods to check the valid values.
348    /// More information about this field:
349    /// Maximum length: 64 characters
350    pub payment_method_type: String,
351}
352
353impl From<&CashierPaymentSimple> for PaymentMethod {
354    fn from(value: &CashierPaymentSimple) -> Self {
355        Self {
356            payment_method_type: String::from("ALIPAY_CN"),
357        }
358    }
359}
360
361/// The order information, such as buyer, merchant, goods, amount, shipping information, and purchase environment. This field is used for different purposes:
362/// During the payment process, this field is mainly used by Alipay for risk control or anti-money laundering.
363/// After the payment is completed, this field is used for recording and reporting purposes such as purchase tracking and regulatory reporting.
364///
365/// skip attributes
366/// - goods
367/// - buyer
368/// - merchant
369/// - extendInfo
370#[derive(Serialize)]
371#[serde(rename_all = "camelCase")]
372pub struct Order {
373    /// The order amount of the merchant that directly provides services or goods to the customer. This field is used for user consumption records display or payment results page.
374    pub order_amount: Amount,
375    pub order_description: String,
376    pub reference_order_id: String,
377}
378
379impl From<&CashierPaymentSimple> for Order {
380    fn from(value: &CashierPaymentSimple) -> Self {
381        let CashierPaymentSimple {
382            reference_order_id,
383            order_description,
384            ..
385        } = value;
386        let roi = reference_order_id.clone().unwrap_or(String::from(""));
387        let od = order_description.clone();
388        let order_amount = Amount::from(value);
389        Self {
390            order_amount,
391            order_description: od,
392            reference_order_id: roi,
393        }
394    }
395}
396
397/// The settlement strategy for the payment request.
398#[derive(Serialize)]
399#[serde(rename_all = "camelCase")]
400pub struct SettlementStrategy {
401    /// The ISO currency code of the currency that the merchant wants to be settled against. The field is required if the merchant signed up for multiple currencies to settle.
402    settlement_currency: String,
403}
404
405impl From<&CashierPaymentSimple> for SettlementStrategy {
406    fn from(value: &CashierPaymentSimple) -> Self {
407        let CashierPaymentSimple { currency, .. } = value;
408
409        Self {
410            settlement_currency: currency.clone(),
411        }
412    }
413}
414
415/// Alipay Pay Response
416#[derive(Serialize, Deserialize, Debug)]
417#[serde(rename_all = "camelCase")]
418pub struct Response {
419    result: ResponseResult,
420    payment_request_id: Option<String>,
421    payment_id: Option<String>,
422    payment_amount: Option<Amount>,
423    actual_payment_amount: Option<Amount>,
424    payment_data: Option<String>,
425    payment_create_time: Option<DateTime<Utc>>,
426    psp_customer_info: Option<PspCustomerInfo>,
427    order_code_form: Option<OrderCodeForm>,
428    // this field is actually option, do NOT trust the Alipay API doc
429    gross_settlement_amount: Option<Amount>,
430    // this field is actually option, do NOT trust the Alipay API doc
431    settlement_quote: Option<SettlementQuote>,
432    app_identifier: Option<String>,
433    applink_url: Option<String>,
434    normal_url: Option<String>,
435    scheme_url: Option<String>,
436    payment_result_info: Option<PaymentResultInfo>,
437    refund_amount: Option<RefundAmount>,
438    refund_time: Option<DateTime<Utc>>,
439    refund_request_id: Option<String>,
440    refund_id: Option<String>,
441    payment_status: Option<PaymentStatus>,
442    payment_result_code: Option<String>,
443    payment_result_message: Option<String>,
444    payment_time: Option<DateTime<Utc>>,
445    redirect_action_form: Option<RedirectActionForm>,
446    acquirer_reference_no: Option<String>,
447    transactions: Option<Vec<Transactions>>,
448    customs_declaration_amount: Option<Amount>,
449}
450
451// impl Signable for Response {
452//     fn get_value(&self) -> Value {
453//         serde_json::to_value(self).unwrap()
454//     }
455// }
456
457impl Response {
458    pub fn result(&self) -> &ResponseResult {
459        &self.result
460    }
461    pub fn payment_request_id(&self) -> &Option<String> {
462        &self.payment_request_id
463    }
464    pub fn payment_id(&self) -> &Option<String> {
465        &self.payment_id
466    }
467    pub fn is_success(&self) -> bool {
468        self.result.result_status == ResultStatus::S
469    }
470    pub fn is_processing(&self) -> bool {
471        self.result.result_code == ResultCode::PAYMENT_IN_PROCESS
472    }
473    pub fn get_error(&self) -> Option<Error> {
474        self.result.get_error()
475    }
476    pub fn get_payment_create_time(&self) -> Option<DateTime<Utc>> {
477        self.payment_create_time
478    }
479    pub fn get_normal_url(&self) -> &Option<String> {
480        &self.normal_url
481    }
482    pub fn get_order_code_form(&self) -> &Option<OrderCodeForm> {
483        &self.order_code_form
484    }
485    pub fn get_refund_amount(&self) -> &Option<RefundAmount> {
486        &self.refund_amount
487    }
488    pub fn get_refund_time(&self) -> &Option<DateTime<Utc>> {
489        &self.refund_time
490    }
491    pub fn get_refund_request_id(&self) -> &Option<String> {
492        &self.refund_request_id
493    }
494    pub fn get_refund_id(&self) -> &Option<String> {
495        &self.refund_id
496    }
497    pub fn get_payment_status(&self) -> &Option<PaymentStatus> {
498        &self.payment_status
499    }
500    pub fn get_payment_result_code(&self) -> &Option<String> {
501        &self.payment_result_code
502    }
503    pub fn get_payment_result_message(&self) -> &Option<String> {
504        &self.payment_result_message
505    }
506    pub fn get_payment_time(&self) -> &Option<DateTime<Utc>> {
507        &self.payment_time
508    }
509    pub fn get_transactions(&self) -> &Option<Vec<Transactions>> {
510        &self.transactions
511    }
512    pub fn get_payment_amount(&self) -> &Option<Amount> {
513        &self.payment_amount
514    }
515    pub fn get_actual_payment_amount(&self) -> &Option<Amount> {
516        &self.actual_payment_amount
517    }
518}
519
520/// The result of the API call.
521#[derive(Deserialize, Serialize, Debug, Clone)]
522#[serde(rename_all = "camelCase")]
523pub struct ResponseResult {
524    /// Result code. The result code that might be returned are listed in the Result/Error codes table on this page.
525    /// More information about this field:
526    /// Maximum length: 64 characters
527    pub result_code: ResultCode,
528    pub result_status: ResultStatus,
529    pub result_message: String,
530}
531
532impl ResponseResult {
533    pub fn get_error(&self) -> Option<Error> {
534        if self.result_code == ResultCode::PAYMENT_IN_PROCESS {
535            // payment in process will have a result status code as U
536            return None;
537        } else if self.result_code == ResultCode::UNKNOWN_EXCEPTION {
538            // First of all, this result code gives no information.
539            // When you encounter this error, you should just retry,
540            // this happens alot in sandbox environment.
541            return Some(Error::Unknown(format!("{}: {}", self.result_code.to_string(), "You should just retry. This error is very normal due to unstable service of Alipay Global. This could happen even when you have all the argument correct. Just retry.")));
542        }
543        match self.result_status {
544            ResultStatus::S => None,
545            ResultStatus::F => Some(Error::Fail(self.result_code.to_string())),
546            ResultStatus::U => Some(Error::Unknown(self.result_code.to_string())),
547        }
548    }
549}
550
551#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Display, Clone)]
552pub enum ResultCode {
553    SUCCESS,
554    ACCESS_DENIED,
555    INVALID_API,
556    CURRENCY_NOT_SUPPORT,
557    EXPIRED_CODE,
558    FRAUD_REJECT,
559    INVALID_ACCESS_TOKEN,
560    INVALID_CONTRACT,
561    INVALID_MERCHANT_STATUS,
562    INVALID_PAYMENT_CODE,
563    INVALID_PAYMENT_METHOD_META_DATA,
564    KEY_NOT_FOUND,
565    MERCHANT_KYB_NOT_QUALIFIED,
566    MERCHANT_NOT_REGISTERED,
567    NO_INTERFACE_DEF,
568    NO_PAY_OPTIONS,
569    ORDER_IS_CANCELED,
570    ORDER_IS_CLOSED,
571    PARAM_ILLEGAL,
572    PAYMENT_AMOUNT_EXCEED_LIMIT,
573    PAYMENT_COUNT_EXCEED_LIMIT,
574    PAYMENT_NOT_QUALIFIED,
575    PROCESS_FAIL,
576    REPEAT_REQ_INCONSISTENT,
577    RISK_REJECT,
578    SETTLE_CONTRACT_NOT_MATCH,
579    SYSTEM_ERROR,
580    USER_AMOUNT_EXCEED_LIMIT,
581    USER_BALANCE_NOT_ENOUGH,
582    USER_KYC_NOT_QUALIFIED,
583    PAYMENT_IN_PROCESS,
584    REQUEST_TRAFFIC_EXCEED_LIMIT,
585    UNKNOWN_EXCEPTION,
586    USER_NOT_EXIST,
587    ORDER_NOT_EXIST,
588    ORDER_STATUS_INVALID,
589    USER_PAYMENT_VERIFICATION_FAILED,
590    USER_STATUS_ABNORMAL,
591    VERIFY_TIMES_EXCEED_LIMIT,
592    VERIFY_UNMATCHED,
593    AUTHENTICATION_REQUIRED,
594    SELECTED_CARD_BRAND_NOT_AVAILABLE,
595    PAYMENT_PROHIBITED,
596    REFUND_AMOUNT_EXCEED,
597    REFUND_WINDOW_EXCEED,
598    REFUND_IN_PROCESS,
599    REFUND_NOT_SUPPORTED,
600}
601
602/// Result status. Valid values are:
603/// S: Indicates that the API call succeeds.
604/// F: Indicates that the API call fails.
605/// U: Indicates that the API call might be successful, in process, or failed. For more details, see Result process logic.
606#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)]
607pub enum ResultStatus {
608    S,
609    F,
610    U,
611}
612/// Information about the order code.
613/// This parameter is returned when the payment method supports providing the related information.  
614#[derive(Deserialize, Debug, Clone, Serialize)]
615#[serde(rename_all = "camelCase")]
616#[cfg_attr(feature = "juniper", derive(GraphQLObject))]
617pub struct OrderCodeForm {
618    expire_time: chrono::DateTime<chrono::Utc>,
619    code_details: Vec<CodeDetail>,
620    extend_info: Option<String>,
621}
622
623/// Details about the code.
624/// More information about this field:
625/// Maximum size: 4 elements
626#[derive(Deserialize, Debug, Clone, Serialize)]
627#[serde(rename_all = "camelCase")]
628#[cfg_attr(feature = "juniper", derive(GraphQLObject))]
629pub struct CodeDetail {
630    code_value: String,
631    display_type: DisplayType,
632}
633
634#[derive(Serialize, Deserialize, Debug)]
635#[serde(rename_all = "camelCase")]
636pub struct PspCustomerInfo {
637    psp_name: Option<String>,
638    psp_customer_id: Option<String>,
639    display_customer_id: Option<String>,
640}
641
642#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
643#[cfg_attr(feature = "juniper", derive(GraphQLEnum))]
644pub enum DisplayType {
645    TEXT,
646    MIDDLEIMAGE,
647    SMALLIMAGE,
648    BIGIMAGE,
649}
650
651/// The exchange rate between the settlement currency and transaction currency. This field is returned when grossSettlementAmount is returned.
652#[derive(Serialize, Deserialize, Debug)]
653#[serde(rename_all = "camelCase")]
654pub struct SettlementQuote {
655    guaranteed: Option<bool>,
656    quote_id: Option<String>,
657    quote_currency_pair: String,
658    quote_price: i32,
659    quote_start_time: Option<DateTime<Utc>>,
660    quote_expiry_time: Option<DateTime<Utc>>,
661}
662
663/// The payment result information.
664/// This parameter may be returned when the value of paymentMethodType is CARD.  
665#[derive(Serialize, Deserialize, Debug)]
666#[serde(rename_all = "camelCase")]
667pub struct PaymentResultInfo {
668    avs_result_raw: Option<String>,
669    cvv_result_raw: Option<String>,
670    network_transaction_id: Option<String>,
671}
672
673#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
674pub enum PaymentStatus {
675    SUCCESS,
676    FAIL,
677    PROCESSING,
678    CANCELLED,
679    PENDING,
680}
681
682#[derive(Serialize, Deserialize, Debug)]
683#[serde(rename_all = "camelCase")]
684pub struct RedirectActionForm {
685    method: RedirectActionFormMethod,
686    parameters: Option<String>,
687    redirect_url: String,
688    action_form_type: Option<String>,
689}
690
691#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
692pub enum RedirectActionFormMethod {
693    POST,
694    GET,
695}
696
697#[derive(Serialize, Deserialize, Debug, Clone)]
698#[serde(rename_all = "camelCase")]
699pub struct Transactions {
700    transaction_result: ResponseResult,
701    transaction_id: String,
702    transaction_type: String,
703    transaction_status: String,
704    transaction_amount: Amount,
705    transaction_request_id: String,
706}
707
708#[derive(Serialize)]
709#[serde(rename_all = "camelCase")]
710pub struct CashierPaymentRefundSimple {
711    pub refund_request_id: String,
712    pub payment_id: String,
713    pub amount: i32,
714    pub currency: String,
715}
716
717#[derive(Serialize, Deserialize, Debug, Clone)]
718#[serde(rename_all = "camelCase")]
719pub struct RefundAmount {
720    value: String,
721    currency: String,
722}
723
724impl From<&CashierPaymentRefundSimple> for RefundAmount {
725    fn from(value: &CashierPaymentRefundSimple) -> Self {
726        let CashierPaymentRefundSimple {
727            amount, currency, ..
728        } = value;
729        Self {
730            value: amount.to_string().clone(),
731            currency: currency.clone(),
732        }
733    }
734}
735
736#[derive(Serialize)]
737#[serde(rename_all = "camelCase")]
738pub struct CashierPaymentRefundFull {
739    payment_id: String,
740    refund_request_id: String,
741    refund_amount: RefundAmount,
742}
743
744impl CashierPaymentRefundFull {
745    pub fn to_string(&self) -> String {
746        serde_json::to_value(self).unwrap().to_string()
747    }
748}
749
750impl From<&CashierPaymentRefundSimple> for CashierPaymentRefundFull {
751    fn from(value: &CashierPaymentRefundSimple) -> Self {
752        let CashierPaymentRefundSimple {
753            refund_request_id,
754            payment_id,
755            ..
756        } = value;
757        let refund_amount = RefundAmount::from(value);
758        Self {
759            payment_id: payment_id.clone(),
760            refund_request_id: refund_request_id.clone(),
761            refund_amount: refund_amount,
762        }
763    }
764}
765
766impl Signable for CashierPaymentRefundFull {
767    fn get_value(&self) -> Value {
768        serde_json::to_value(self).unwrap()
769    }
770}
771
772#[derive(Serialize, Deserialize)]
773#[serde(rename_all = "camelCase")]
774pub struct CashierPaymentInquiry {
775    pub payment_request_id: Option<String>,
776    pub payment_id: Option<String>,
777}
778
779impl CashierPaymentInquiry {
780    pub fn to_string(&self) -> String {
781        serde_json::to_value(self).unwrap().to_string()
782    }
783}
784
785impl Signable for CashierPaymentInquiry {
786    fn get_value(&self) -> Value {
787        serde_json::to_value(self).unwrap()
788    }
789}
790
791#[derive(Debug, Serialize, Deserialize, Clone)]
792pub struct PaymentAmount {
793    currency: String,
794    value: String,
795}
796
797#[derive(Debug, Serialize, Deserialize, Clone)]
798pub struct NotifyPayment {
799    capture_amount: PaymentAmount,
800    notify_type: String,
801    capture_id: String,
802    capture_request_id: String,
803    capture_time: String,
804    payment_id: String,
805    result: ResponseResult,
806}
807
808#[derive(Debug, Serialize, Deserialize, Clone)]
809pub struct WebhookData {
810    pub method: String,
811    pub path: String,
812    pub request_time: String,
813    pub header_signature: String,
814    pub client_id: String,
815    pub request_body: String,
816}
817
818#[derive(Debug, Serialize, Deserialize, Clone)]
819pub struct WebhookResponse {
820    pub full_signature: String,
821    pub client_id: String,
822    pub response_time: String,
823    pub body: String,
824}
825
826#[derive(Debug, Serialize, Deserialize, Clone)]
827pub struct WebhookResponseInput {
828    pub method: String,
829    pub path: String,
830    pub client_id: String,
831}
832
833#[derive(Deserialize, Serialize, Debug, Clone)]
834#[serde(rename_all = "camelCase")]
835pub struct WebhookResponseResult {
836    pub result: ResponseResult,
837}
838
839impl Signable for WebhookResponseResult {
840    fn get_value(&self) -> Value {
841        serde_json::to_value(self).unwrap()
842    }
843}