use crate::serde_help::*;
use builder_pattern::Builder;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrderRequest {
pub cart: RenderedCart,
#[default(CurrencyCode::Rub)]
pub currency_code: CurrencyCode,
#[into]
pub order_id: String,
#[default(vec![AvailablePaymentMethod::Card])]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub available_payment_methods: Vec<AvailablePaymentMethod>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_phone: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub extensions: Option<OrderExtensions>,
#[default(false)]
pub is_prepayment: bool,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub order_source: Option<OrderSource>,
#[default(Some(PreferredPaymentMethod::FullPayment))]
pub preferred_payment_method: Option<PreferredPaymentMethod>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub public_constructor: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub purpose: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub redirect_urls: Option<MerchantRedirectUrls>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub risk: Option<MerchantRiskInfo>,
#[default(Some(1800))]
pub ttl: Option<u32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub uniqr_metadata: Option<UniqrMetadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct RenderedCart {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub items: Vec<RenderedCartItem>,
pub total: CartTotal,
#[into]
pub external_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct RenderedCartItem {
#[into]
pub product_id: String,
pub quantity: ItemQuantity,
#[into]
pub title: String,
#[into]
#[serde(with = "string_as_float")]
pub total: f64,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub discounted_unit_price: Option<f64>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub features: Option<CartItemFeatures>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub points_amount: Option<f64>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub receipt: Option<ItemReceipt>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub subtotal: Option<f64>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub unit_price: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct CartTotal {
#[into]
#[serde(with = "string_as_float")]
pub amount: f64,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub points_amount: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct BillingReport {
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub branch_id: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub manager_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct QRData {
#[into]
pub token: String, }
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct SMSOffer {
#[into]
pub phone: String, }
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct CustomerAggregates {
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub amount_first_successful_order: Option<f64>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub amount_latest_successful_order: Option<f64>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub cookie: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub days_since_last_password_reset: Option<i32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub failed_login_attempts_one_day: Option<i32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub failed_login_attempts_seven_days: Option<i32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub first_successful_order_date: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub historical_cookie_login: Option<bool>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub historical_device_login: Option<bool>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub last_password_reset_date: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub latest_successful_order_last_year_date: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub previous_successful_orders_at_same_address: Option<bool>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "option_string_as_float")]
pub redemption_rate_last_half_year: Option<f64>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub registration_date: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct PeriodCheckAggregates {
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub successful_orders_count_nine_months: Option<i32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub successful_orders_count_one_month: Option<i32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub successful_orders_count_six_months: Option<i32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub successful_orders_count_three_months: Option<i32>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub successful_orders_count_twelve_months: Option<i32>,
#[serde(with = "option_string_as_float")]
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub total_amount_successful_orders_nine_months: Option<f64>,
#[serde(with = "option_string_as_float")]
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub total_amount_successful_orders_one_month: Option<f64>,
#[serde(with = "option_string_as_float")]
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub total_amount_successful_orders_six_months: Option<f64>,
#[serde(with = "option_string_as_float")]
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub total_amount_successful_orders_three_months: Option<f64>,
#[serde(with = "option_string_as_float")]
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub total_amount_successful_orders_twelve_months: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct ItemQuantity {
#[serde(with = "string_as_float")]
#[into]
pub count: f64,
#[serde(with = "string_as_float")]
#[into]
pub available: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct CartItemFeatures {
#[default(Some(false))]
pub points_disabled: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct ItemReceipt {
pub tax: Tax,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub agent: Option<Agent>,
#[serde(with = "option_string_as_float")]
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub excise: Option<f64>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub mark_quantity: Option<MarkQuantity>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub measure: Option<Measure>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub payment_method_type: Option<PaymentMethodType>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub payment_subject_type: Option<PaymentSubjectType>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub product_code: Option<String>, #[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub supplier: Option<Supplier>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>, }
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct Agent {
pub agent_type: AgentType,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub operation: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub payments_operator: Option<PaymentsOperator>,
#[default(vec![])]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub phones: Vec<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub transfer_operator: Option<TransferOperator>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct MarkQuantity {
#[into]
pub denominator: i32,
#[into]
pub numerator: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct Supplier {
#[into]
pub inn: String,
#[into]
pub name: String,
#[default(vec![])]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub phones: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct PaymentsOperator {
#[default(vec![])]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub phones: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct TransferOperator {
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[into]
pub inn: String,
#[into]
pub name: String,
#[default(vec![])]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub phones: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct OrderExtensions {
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_report: Option<BillingReport>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub qr_data: Option<QRData>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub sms_offer: Option<SMSOffer>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct MerchantRedirectUrls {
#[into]
pub on_error: String,
#[into]
pub on_success: String,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub on_abort: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct MerchantRiskInfo {
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub customer_aggregates: Option<CustomerAggregates>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub device_id: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub is_express_shipping: Option<bool>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub period_check_aggregates: Option<PeriodCheckAggregates>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub shipping_address: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub shipping_phone: Option<String>,
#[default(None)]
#[serde(skip_serializing_if = "Option::is_none")]
pub shipping_type: Option<ShippingType>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct UniqrMetadata {
#[into]
pub uni_qr_id: String,
}
#[derive(Debug, Clone, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Tax {
Vat20 = 1,
Vat10 = 2,
Vat20_120 = 3,
Vat10_110 = 4,
Vat0 = 5,
NoVat = 6,
Vat7 = 7,
Vat5 = 8,
Vat7_107 = 9,
Vat5_105 = 10,
}
#[derive(Debug, Clone, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum PaymentMethodType {
FullPrepayment = 1,
PartialPrepayment = 2,
Advance = 3,
FullPayment = 4,
PartialPayment = 5,
CreditTransfer = 6,
CreditPayment = 7,
}
#[derive(Debug, Clone, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum PaymentSubjectType {
Goods = 1,
ExciseGoods = 2,
Work = 3,
Service = 4,
GamblingBet = 5,
GamblingWin = 6,
LotteryTicket = 7,
LotteryWin = 8,
IntellectualProperty = 9,
Payment = 10,
AgencyFee = 11,
Composite = 12,
Other = 13,
PropertyRight = 14,
NonOperatingIncome = 15,
InsuranceContributionsTaxReduction = 16,
TradeFee = 17,
ResortFee = 18,
Deposit = 19,
ExpenseTaxReduction = 20,
PensionInsuranceIP = 21,
PensionInsurance = 22,
MedicalInsuranceIP = 23,
MedicalInsurance = 24,
SocialInsurance = 25,
CasinoPayment = 26,
}
#[derive(Debug, Clone, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Measure {
Units = 0,
Gram = 10,
Kilogram = 11,
Tonne = 12,
Centimeter = 20,
Decimeter = 21,
Meter = 22,
SquareCentimeter = 30,
SquareDecimeter = 31,
SquareMeter = 32,
Milliliter = 40,
Liter = 41,
CubicMeter = 42,
KilowattHour = 50,
Gigacalorie = 51,
Day = 70,
Hour = 71,
Minute = 72,
Second = 73,
Kilobyte = 80,
Megabyte = 81,
Gigabyte = 82,
Terabyte = 83,
Other = 255,
}
#[derive(Debug, Clone, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum AgentType {
BankPaymentAgent = 1,
BankPaymentSubagent = 2,
PaymentAgent = 3,
PaymentSubagent = 4,
Attorney = 5,
Commissioner = 6,
OtherAgent = 7,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ShippingType {
#[serde(rename = "COURIER")]
Courier,
#[serde(rename = "PICKUP")]
Pickup,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PreferredPaymentMethod {
#[serde(rename = "FULLPAYMENT")]
FullPayment,
#[serde(rename = "SPLIT")]
Split,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum OrderSource {
#[serde(rename = "WEBSITE")]
Website,
#[serde(rename = "APP")]
App,
#[serde(rename = "CRM")]
Crm,
#[serde(rename = "CASH_REGISTER")]
CashRegister,
#[serde(rename = "CMS_PLUGIN")]
CmsPlugin,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum AvailablePaymentMethod {
#[serde(rename = "CARD")]
Card,
#[serde(rename = "SPLIT")]
Split,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)]
pub enum CurrencyCode {
#[default]
#[serde(rename = "RUB")]
Rub,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrderResponse {
pub payment_url: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_create_order_request() {
let json = r#"
{
"availablePaymentMethods": [],
"billingPhone": "string",
"cart": {
"externalId": "string",
"items": [
{
"description": "string",
"discountedUnitPrice": "123.45",
"features": {
"pointsDisabled": false
},
"pointsAmount": "123.46",
"productId": "string",
"quantity": {
"available": "123.45",
"count": "123.45"
},
"receipt": {
"agent": {
"agentType": 1,
"operation": "string",
"paymentsOperator": {
"phones": [
"string"
]
},
"phones": [
"string"
],
"transferOperator": {
"address": "string",
"inn": "string",
"name": "string",
"phones": [
"string"
]
}
},
"excise": "123.45",
"markQuantity": {
"denominator": 0,
"numerator": 0
},
"measure": 0,
"paymentMethodType": 1,
"paymentSubjectType": 1,
"productCode": "string",
"supplier": {
"inn": "string",
"name": "string",
"phones": [
"string"
]
},
"tax": 1,
"title": "string"
},
"subtotal": "123.45",
"title": "string",
"total": "123.45",
"unitPrice": "123.45"
}
],
"total": {
"amount": "123.45",
"pointsAmount": "123.45"
}
},
"currencyCode": "RUB",
"extensions": {
"billingReport": {
"branchId": null,
"managerId": null
},
"qrData": {
"token": "string"
},
"smsOffer": {
"phone": "string"
}
},
"isPrepayment": false,
"metadata": "string",
"orderId": "string",
"orderSource": "WEBSITE",
"preferredPaymentMethod": "FULLPAYMENT",
"publicConstructor": "string",
"purpose": "string",
"redirectUrls": {
"onAbort": "string",
"onError": "string",
"onSuccess": "string"
},
"risk": {
"billingPhone": "string",
"customerAggregates": {
"amountFirstSuccessfulOrder": "123.45",
"amountLatestSuccessfulOrder": "123.45",
"cookie": "string",
"daysSinceLastPasswordReset": 0,
"failedLoginAttemptsOneDay": 0,
"failedLoginAttemptsSevenDays": 0,
"firstSuccessfulOrderDate": "string",
"historicalCookieLogin": false,
"historicalDeviceLogin": false,
"lastPasswordResetDate": "string",
"latestSuccessfulOrderLastYearDate": "string",
"previousSuccessfulOrdersAtSameAddress": false,
"redemptionRateLastHalfYear": "123.45",
"registrationDate": "string"
},
"deviceId": "string",
"isExpressShipping": false,
"periodCheckAggregates": {
"successfulOrdersCountNineMonths": 0,
"successfulOrdersCountOneMonth": 0,
"successfulOrdersCountSixMonths": 0,
"successfulOrdersCountThreeMonths": 0,
"successfulOrdersCountTwelveMonths": 0,
"totalAmountSuccessfulOrdersNineMonths": "123.45",
"totalAmountSuccessfulOrdersOneMonth": "123.45",
"totalAmountSuccessfulOrdersSixMonths": "123.45",
"totalAmountSuccessfulOrdersThreeMonths": "123.45",
"totalAmountSuccessfulOrdersTwelveMonths": "123.45"
},
"shippingAddress": "string",
"shippingPhone": "string",
"shippingType": "COURIER"
},
"ttl": 1800,
"uniqrMetadata": {
"uniQrId": "string"
}
}
"#;
let parsed: CreateOrderRequest = serde_json::from_str(json).unwrap();
assert_eq!(parsed.billing_phone, Some("string".to_string()));
assert_eq!(parsed.cart.external_id, "string".to_string());
assert_eq!(parsed.currency_code, CurrencyCode::Rub);
assert_eq!(parsed.order_id, "string".to_string());
}
#[test]
fn test_create_order() {
let request = CreateOrderRequest::new()
.cart(
RenderedCart::new()
.items(vec![])
.external_id("123")
.total(CartTotal::new().amount(100.0).build())
.build(),
)
.order_id("123")
.build();
let json = serde_json::to_string(&request).unwrap();
println!("{}", json);
}
}