From 383b14b86a062a5e680c2ec66e49e15f1f6eea2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Wed, 4 May 2022 07:40:34 +0200 Subject: [PATCH] Remove pay_u module --- pay_u/src/credit.rs | 77 - pay_u/src/deserialize.rs | 162 -- pay_u/src/lib.rs | 1475 ----------------- pay_u/src/notify.rs | 161 -- pay_u/src/req.rs | 381 ----- pay_u/src/res.rs | 144 -- pay_u/src/serialize.rs | 25 - pay_u/tests/responses/accepted_refund.json | 17 - pay_u/tests/responses/cancel.json | 21 - .../tests/responses/completed_cart_token.json | 38 - .../responses/completed_installments.json | 38 - pay_u/tests/responses/completed_pbl.json | 38 - .../tests/responses/custom_code_literal.json | 9 - pay_u/tests/responses/refund.json | 14 - pay_u/tests/responses/rejection.json | 9 - 15 files changed, 2609 deletions(-) delete mode 100644 pay_u/src/credit.rs delete mode 100644 pay_u/src/deserialize.rs delete mode 100644 pay_u/src/lib.rs delete mode 100644 pay_u/src/notify.rs delete mode 100644 pay_u/src/req.rs delete mode 100644 pay_u/src/res.rs delete mode 100644 pay_u/src/serialize.rs delete mode 100644 pay_u/tests/responses/accepted_refund.json delete mode 100644 pay_u/tests/responses/cancel.json delete mode 100644 pay_u/tests/responses/completed_cart_token.json delete mode 100644 pay_u/tests/responses/completed_installments.json delete mode 100644 pay_u/tests/responses/completed_pbl.json delete mode 100644 pay_u/tests/responses/custom_code_literal.json delete mode 100644 pay_u/tests/responses/refund.json delete mode 100644 pay_u/tests/responses/rejection.json diff --git a/pay_u/src/credit.rs b/pay_u/src/credit.rs deleted file mode 100644 index 3fa8b7c..0000000 --- a/pay_u/src/credit.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! This module allow to create credit request during create order request - -use serde::{Deserialize, Serialize}; - -use crate::{Address, ShoppingCart}; - -/// Describe customer during credit request -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Applicant { - /// Applicant’s email address - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - email: Option, - /// Applicant’s phone number - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - phone: Option, - /// Applicant’s first name - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - first_name: Option, - /// Applicant’s last name - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - last_name: Option, - /// Language code, ISO-639-1 compliant. Denotes the language version of - /// PayU hosted payment page and of e-mail messages sent from PayU to the - /// payer (supported values are here). - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - language: Option, - /// National Identification Number - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - nin: Option, - /// Section containing data about applicant’s address. - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - address: Option
, - /// Additional information about person applying for credit. - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - additional_info: Option, -} - -/// Allow to create credit request -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "camelCase")] -pub struct Credit { - /// Section containing data of the ordered products - #[serde(skip_serializing_if = "Option::is_none")] - shopping_carts: Option>, - /// Section containing data of person applying for a credit - #[serde(skip_serializing_if = "Option::is_none")] - applicant: Option, -} - -impl Credit { - pub fn with_shopping_carts(mut self, shopping_carts: ShoppingCarts) -> Self - where - ShoppingCarts: Iterator, - { - self.shopping_carts = Some(shopping_carts.collect()); - self - } -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ApplicantAdditionalInfo { - /// Information whether there were previous, successfully completed orders - /// for applicant. - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - has_successfully_finished_order_in_shop: Option, -} diff --git a/pay_u/src/deserialize.rs b/pay_u/src/deserialize.rs deleted file mode 100644 index 9aa1736..0000000 --- a/pay_u/src/deserialize.rs +++ /dev/null @@ -1,162 +0,0 @@ -use std::fmt; - -use serde::de::{self, Error, Visitor}; - -pub(crate) fn deserialize_i32<'de, D>(d: D) -> std::result::Result -where - D: serde::Deserializer<'de>, -{ - d.deserialize_string(I32Visitor) -} - -pub(crate) fn deserialize_i32_newtype<'de, N: From, D>( - d: D, -) -> std::result::Result -where - D: serde::Deserializer<'de>, -{ - d.deserialize_string(I32Visitor).map(N::from) -} - -pub(crate) fn deserialize_u32<'de, D>(d: D) -> std::result::Result -where - D: serde::Deserializer<'de>, -{ - d.deserialize_string(U32Visitor) -} - -struct I32Visitor; - -impl<'de> Visitor<'de> for I32Visitor { - type Value = i32; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an integer between -2^31 and 2^31") - } - - fn visit_i8(self, value: i8) -> Result - where - E: de::Error, - { - Ok(i32::from(value)) - } - - fn visit_i32(self, value: i32) -> Result - where - E: de::Error, - { - Ok(value) - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - use std::i32; - if value >= i64::from(i32::MIN) && value <= i64::from(i32::MAX) { - Ok(value as i32) - } else { - Err(E::custom(format!("i32 out of range: {}", value))) - } - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - if let Ok(value) = v.parse::() { - Ok(value) - } else { - Err(E::custom(format!("str does not contains valid i32: {}", v))) - } - } - - fn visit_string(self, v: String) -> Result - where - E: Error, - { - if let Ok(value) = v.parse::() { - Ok(value) - } else { - Err(E::custom(format!( - "string does not contains valid i32: {}", - v - ))) - } - } - - // Similar for other methods: - // - visit_i16 - // - visit_u8 - // - visit_u16 - // - visit_u32 - // - visit_u64 -} - -struct U32Visitor; - -impl<'de> Visitor<'de> for U32Visitor { - type Value = u32; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an integer between -2^31 and 2^31") - } - - fn visit_u8(self, value: u8) -> Result - where - E: de::Error, - { - Ok(u32::from(value)) - } - - fn visit_u32(self, value: u32) -> Result - where - E: de::Error, - { - Ok(value) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - use std::u32; - if value >= u64::from(u32::MIN) && value <= u64::from(u32::MAX) { - Ok(value as u32) - } else { - Err(E::custom(format!("i32 out of range: {}", value))) - } - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - if let Ok(value) = v.parse::() { - Ok(value) - } else { - Err(E::custom(format!("str does not contains valid i32: {}", v))) - } - } - - fn visit_string(self, v: String) -> Result - where - E: Error, - { - if let Ok(value) = v.parse::() { - Ok(value) - } else { - Err(E::custom(format!( - "string does not contains valid i32: {}", - v - ))) - } - } - - // Similar for other methods: - // - visit_i16 - // - visit_u8 - // - visit_u16 - // - visit_u32 - // - visit_u64 -} diff --git a/pay_u/src/lib.rs b/pay_u/src/lib.rs deleted file mode 100644 index 1a9080f..0000000 --- a/pay_u/src/lib.rs +++ /dev/null @@ -1,1475 +0,0 @@ -//! This is unofficial client support integration with the REST API 2.1 -//! protocol. It presents various methods of implementing online payments via -//! different PayU services and is dedicated primarily to developers wanting to -//! implement the PayU payment services. - -pub mod credit; -mod deserialize; -pub mod notify; -pub mod req; -pub mod res; -mod serialize; - -use std::sync::Arc; - -use reqwest::redirect; -use serde::{Deserialize, Serialize}; - -use crate::res::OrdersInfo; - -macro_rules! get_client { - ($self:expr) => {{ - #[cfg(feature = "single-client")] - { - $self.client.clone() - } - #[cfg(not(feature = "single-client"))] - { - Client::build_client() - } - }}; -} - -pub static SUCCESS: &str = "SUCCESS"; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Client is not authorized. No bearer token available")] - NoToken, - #[error("Invalid customer ip. IP 0.0.0.0 is not acceptable")] - CustomerIp, - #[error("{0}")] - Io(#[from] std::io::Error), - #[error("Total value is not sum of products price")] - IncorrectTotal, - #[error("{0}")] - Reqwest(#[from] reqwest::Error), - #[error("Buyer is required to place an order")] - NoBuyer, - #[error("Description is required to place an order")] - NoDescription, - #[error("Client is not authorized")] - Unauthorized, - #[error("Refund returned invalid response")] - Refund, - #[error("Create order returned invalid response")] - CreateOrder, - #[error("Failed to fetch order transactions")] - OrderTransactions, - #[error("Failed to fetch order details")] - OrderDetails, - #[error("Failed to fetch order refunds")] - OrderRefunds, - #[error("PayU rejected to create order with status {status_code:?}")] - CreateFailed { - status_code: String, - status_desc: Option, - code: Option, - severity: Option, - code_literal: Option, - }, - #[error("PayU rejected to perform refund with status {status_code:?}")] - RefundFailed { - status_code: String, - status_desc: Option, - code: Option, - severity: Option, - code_literal: Option, - }, - #[error("PayU rejected order details request with status {status_code:?}")] - OrderDetailsFailed { - status_code: String, - status_desc: Option, - code: Option, - severity: Option, - code_literal: Option, - }, - #[error("PayU rejected order transactions details request with status {status_code:?}")] - OrderTransactionsFailed { - status_code: String, - status_desc: Option, - code: Option, - severity: Option, - code_literal: Option, - }, - #[error("PayU returned order details but without any order")] - NoOrderInDetails, -} - -pub type Result = std::result::Result; - -/// PayU internal order id -/// -/// Unique order identifier -#[derive( - Debug, - Clone, - serde::Deserialize, - serde::Serialize, - derive_more::Display, - derive_more::From, - derive_more::Deref, -)] -#[serde(transparent)] -pub struct OrderId(pub String); - -impl OrderId { - pub fn new>(id: S) -> Self { - Self(id.into()) - } -} - -/// PayU internal merchant id -/// -/// This value is customer identifier -#[derive( - Debug, - serde::Deserialize, - serde::Serialize, - Copy, - Clone, - derive_more::Display, - derive_more::From, - derive_more::Deref, - derive_more::Constructor, -)] -#[serde(transparent)] -pub struct MerchantPosId(pub i32); - -/// Public payu OAuth client identifier -#[derive( - Debug, - Clone, - serde::Deserialize, - serde::Serialize, - derive_more::Display, - derive_more::From, - derive_more::Deref, -)] -#[serde(transparent)] -pub struct ClientId(pub String); - -impl ClientId { - pub fn new>(id: S) -> Self { - Self(id.into()) - } -} - -/// Secret payu OAuth client identifier -#[derive( - Debug, - Clone, - serde::Deserialize, - serde::Serialize, - derive_more::Display, - derive_more::From, - derive_more::Deref, -)] -#[serde(transparent)] -pub struct ClientSecret(pub String); - -impl ClientSecret { - pub fn new>(id: S) -> Self { - Self(id.into()) - } -} - -/// PayU payment status. -/// -/// Each payment is initially Pending and can change according to following -/// graph: -/// -/// -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum PaymentStatus { - /// Payment is currently being processed. - Pending, - /// PayU is currently waiting for the merchant system to receive (capture) - /// the payment. This status is set if auto-receive is disabled on the - /// merchant system. - WaitingForConfirmation, - /// Payment has been accepted. PayU will pay out the funds shortly. - Completed, - /// Payment has been cancelled and the buyer has not been charged (no money - /// was taken from buyer's account). - Canceled, -} - -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum RefundStatus { - /// refund was completed successfully - Finalized, - /// refund was cancelled - Canceled, - /// refund in progress - Pending, - /// PayU is currently waiting for the merchant system to receive (capture) - /// the payment. This status is set if auto-receive is disabled on the - /// merchant system. - WaitingForConfirmation, - /// Payment has been accepted. PayU will pay out the funds shortly. - Completed, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "camelCase")] -pub struct Delivery { - #[serde(skip_serializing_if = "Option::is_none")] - /// Street name - street: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Postal box number - postal_box: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Postal code - postal_code: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// City - city: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Province - state: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Two-letter country code compliant with ISO-3166. - country_code: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Address description - name: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Recipient’s name - recipient_name: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Recipient’s e-mail address - recipient_email: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - /// Recipient’s phone number - recipient_phone: Option, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "camelCase")] -pub struct BuyerShippingAddress { - #[serde(skip_serializing_if = "Option::is_none")] - /// stores the shipping address - delivery: Option, -} - -impl BuyerShippingAddress { - pub fn new_with_delivery(delivery: Delivery) -> Self { - Self { - delivery: Some(delivery), - } - } -} - -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "camelCase")] -pub struct Buyer { - /// Required customer e-mail - #[serde(skip_serializing_if = "Option::is_none")] - email: Option, - /// Required customer phone number - #[serde(skip_serializing_if = "Option::is_none")] - phone: Option, - /// Required customer first name - #[serde(skip_serializing_if = "Option::is_none")] - first_name: Option, - /// Required customer last name - #[serde(skip_serializing_if = "Option::is_none")] - last_name: Option, - /// Required customer language - #[serde(skip_serializing_if = "Option::is_none")] - language: Option, - #[serde(skip_serializing_if = "Option::is_none")] - delivery: Option, -} - -impl Buyer { - pub fn new( - email: Email, - phone: Phone, - first_name: FirstName, - last_name: LastName, - lang: Language, - ) -> Self - where - Email: Into, - Phone: Into, - FirstName: Into, - LastName: Into, - Language: Into, - { - Self { - email: Some(email.into()), - phone: Some(phone.into()), - first_name: Some(first_name.into()), - last_name: Some(last_name.into()), - language: Some(lang.into()), - delivery: None, - } - } - - pub fn email(&self) -> &str { - self.email.as_deref().unwrap_or_default() - } - pub fn with_email(mut self, email: S) -> Self - where - S: Into, - { - self.email = Some(email.into()); - self - } - pub fn phone(&self) -> &str { - self.phone.as_deref().unwrap_or_default() - } - pub fn with_phone(mut self, phone: S) -> Self - where - S: Into, - { - self.phone = Some(phone.into()); - self - } - pub fn first_name(&self) -> &str { - self.first_name.as_deref().unwrap_or_default() - } - pub fn with_first_name(mut self, first_name: S) -> Self - where - S: Into, - { - self.first_name = Some(first_name.into()); - self - } - pub fn last_name(&self) -> &str { - self.last_name.as_deref().unwrap_or_default() - } - pub fn with_last_name(mut self, last_name: S) -> Self - where - S: Into, - { - self.last_name = Some(last_name.into()); - self - } - pub fn language(&self) -> &str { - self.language.as_deref().unwrap_or_default() - } - pub fn with_language(mut self, language: S) -> Self - where - S: Into, - { - self.language = Some(language.into()); - self - } - - pub fn with_delivery(mut self, delivery: Delivery) -> Self { - self.delivery = Some(BuyerShippingAddress::new_with_delivery(delivery)); - self - } -} - -pub type Price = i32; -pub type Quantity = u32; - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Product { - pub name: String, - #[serde( - serialize_with = "serialize::serialize_i32", - deserialize_with = "deserialize::deserialize_i32" - )] - pub unit_price: Price, - #[serde( - serialize_with = "serialize::serialize_u32", - deserialize_with = "deserialize::deserialize_u32" - )] - pub quantity: Quantity, - /// Product type, which can be virtual or material; (possible values true or - /// false). - #[serde(rename = "virtual", skip_serializing_if = "Option::is_none")] - pub virtual_product: Option, - /// Marketplace date from which the product (or offer) is available, ISO - /// format applies, e.g. "2019-03-27T10:57:59.000+01:00". - #[serde(skip_serializing_if = "Option::is_none")] - pub listing_date: Option, -} - -impl Product { - pub fn new>(name: Name, unit_price: Price, quantity: Quantity) -> Self { - Self { - name: name.into(), - unit_price, - quantity, - virtual_product: None, - listing_date: None, - } - } - - /// Product type, which can be virtual or material; (possible values true or - /// false). - pub fn into_virtual(mut self) -> Self { - self.virtual_product = Some(true); - self - } - - /// Product type, which can be virtual or material; (possible values true or - /// false). - pub fn non_virtual(mut self) -> Self { - self.virtual_product = Some(false); - self - } - - /// Marketplace date from which the product (or offer) is available, ISO - /// format applies, e.g. "2019-03-27T10:57:59.000+01:00". - pub fn with_listing_date(mut self, listing_date: chrono::NaiveDateTime) -> Self { - self.listing_date = Some(listing_date); - self - } - - fn erase_listing_date(mut self) -> Self { - self.listing_date = None; - self - } -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ShoppingCart { - /// Section containing data of shipping method. - #[serde(skip_serializing_if = "Option::is_none")] - shopping_method: Option, - /// Section containing data about ordered products. - /// > Note: product objects in the section do not - /// > have a listingDate field - #[serde(skip_serializing_if = "Option::is_none")] - products: Option>, - /// Submerchant identifier. This field should be consistent with field - /// extCustomerId in shoppingCarts section when order is placed in - /// marketplace. - ext_customer_id: String, -} - -impl ShoppingCart { - pub fn new(ext_customer_id: ExtCustomerId) -> Self - where - ExtCustomerId: Into, - { - Self { - shopping_method: None, - ext_customer_id: ext_customer_id.into(), - products: None, - } - } - - pub fn new_with_products( - ext_customer_id: ExtCustomerId, - products: Products, - ) -> Self - where - ExtCustomerId: Into, - Products: Iterator, - { - Self { - shopping_method: None, - ext_customer_id: ext_customer_id.into(), - products: Some(products.map(Product::erase_listing_date).collect()), - } - } - - pub fn with_products(mut self, products: Products) -> Self - where - Products: Iterator, - { - self.products = Some(products.map(Product::erase_listing_date).collect()); - self - } - - /// Section containing data of shipping method. - pub fn shopping_method(&self) -> &Option { - &self.shopping_method - } - - /// Section containing data about ordered products. - /// > Note: product objects in the section do not - /// > have a listingDate field - pub fn products(&self) -> &Option> { - &self.products - } - - /// Submerchant identifier. This field should be consistent with field - /// extCustomerId in shoppingCarts section when order is placed in - /// marketplace. - pub fn ext_customer_id(&self) -> &String { - &self.ext_customer_id - } -} - -/// Type of shipment -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ShoppingMethodType { - Courier, - CollectionPointPickup, - ParcelLocker, - StorePickup, -} - -/// Delivery address -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Address { - /// The full name of the pickup point, including its unique identifier, e.g. - /// „Parcel locker POZ29A”. - #[serde(skip_serializing_if = "Option::is_none")] - pub point_id: Option, - /// Street name, possibly including house and flat number. - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub street: Option, - /// Street number - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub street_no: Option, - /// Flat number - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub flat_no: Option, - /// Postal code - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub postal_code: Option, - /// City - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub city: Option, - /// Two-letter country code compliant with ISO-3166 - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub country_code: Option, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ShoppingMethod { - /// Shipping type - /// Recommended - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub shopping_type: Option, - /// Shipping cost - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub price: Option, - /// Section containing data about shipping address. - /// Recommended - #[serde(skip_serializing_if = "Option::is_none")] - pub address: Option
, -} - -/// MultiUseCartToken -pub mod muct { - use serde::{Deserialize, Serialize}; - - #[derive(Serialize, Deserialize, Debug)] - #[serde(rename_all = "SCREAMING_SNAKE_CASE")] - pub enum CardOnFile { - /// Payment initialized by the card owner who agreed to save card for - /// future use. You can expect full authentication (3D Secure - /// and/or CVV). If you want to use multi-use token (TOKC_) - /// later, you have to be confident, that first payment was - /// successful. Default value for single-use token (TOK_). - /// - /// In case of plain card data payments you should retrieve transaction - /// data to obtain first TransactionId. It should be passed in - /// payMethods.payMethod.card section for transactions marked as - /// STANDARD, STANDARD_CARDHOLDER and STANDARD_MERCHANT; - /// STANDARD_CARDHOLDER - payment with already saved card, - /// initialized by the card owner. This transaction has - /// multi-use token (TOKC_). Depending of payment parameters - /// (e.g. high transaction amount) strong authentication can be - /// expected (3D Secure and/or CVV). Default value for multi-use token - /// (TOKC_); - First, - /// Payment with already saved card, initialized by the shop without the - /// card owner participation. This transaction has multi-use token - /// (TOKC_). By the definition, this payment type does not - /// require strong authentication. You cannot use it if FIRST - /// card-on-file payment failed. - StandardMerchant, - } - - #[derive(Serialize, Deserialize, Debug)] - #[serde(rename_all = "SCREAMING_SNAKE_CASE")] - pub enum Recurring { - /// Payment initialized by the card owner who agreed to save card for - /// future use in recurring plan. You can expect full authentication (3D - /// Secure and/or CVV). If you want to use multi-use token (TOKC_) - /// later, you have to be confident, that first recurring - /// payment was successful. - First, - /// Subsequent recurring payment (user is not present). This transaction - /// has multi use token (TOKC_). You cannot use it if FIRST recurring - /// payment failed. - Standard, - } - - #[derive(Serialize, Deserialize, Debug)] - #[serde(rename_all = "camelCase")] - pub struct MultiUseCartToken { - /// Information about party initializing order: - /// - /// * `FIRST` - payment initialized by the card owner who agreed to save - /// card for future use. You can expect full authentication (3D Secure - /// and/or CVV). If you want to use multi-use token (TOKC_) later, you - /// have to be confident, that first payment was successful. Default - /// value for single-use token (TOK_). - /// - /// In case of plain card data payments you should retrieve - /// transaction data to obtain first TransactionId. It should - /// be passed in payMethods.payMethod.card section for - /// transactions marked as STANDARD, STANDARD_CARDHOLDER and - /// STANDARD_MERCHANT; STANDARD_CARDHOLDER - payment with - /// already saved card, initialized by the card owner. This - /// transaction has multi-use token (TOKC_). Depending of payment - /// parameters (e.g. high transaction amount) strong authentication - /// can be expected (3D Secure and/or CVV). Default value for - /// multi-use token (TOKC_); - /// * `STANDARD_MERCHANT` - payment with already saved card, initialized - /// by the shop without the card owner participation. This transaction - /// has multi-use token (TOKC_). By the definition, this payment type - /// does not require strong authentication. You cannot use it if FIRST - /// card-on-file payment failed. - /// - /// `cardOnFile` parameter cannot be used with recurring parameter. - pub card_on_file: CardOnFile, - /// Marks the order as recurring payment. - /// - /// * `FIRST` - payment initialized by the card owner who agreed to save - /// card for future use in recurring plan. You can expect full - /// authentication (3D Secure and/or CVV). If you want to use - /// multi-use token (TOKC_) later, you have to be confident, that - /// first recurring payment was successful. - /// * `STANDARD` - subsequent recurring payment (user is not present). - /// This transaction has multi use token (TOKC_). You cannot use it if - /// FIRST recurring payment failed. - /// - /// `recurring` parameter cannot be used with cardOnFile parameter. - pub recurring: Recurring, - } -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum PaymentType { - Pbl, - CardToken, - Installments, -} - -/// Wrapper around pay method. It's used only for deserializing notifications -/// -/// # Examples -/// -/// ``` -/// # use pay_u::PayMethod; -/// let method: PayMethod = serde_json::from_str(r#" -/// { -/// "type": "INSTALLMENTS" -/// } -/// "#).unwrap(); -/// ``` -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct PayMethod { - #[serde(rename = "type")] - pub payment_type: PaymentType, -} - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Status { - /// One of - /// * `PENDING`: Payment is currently being processed. - /// * `WAITING_FOR_CONFIRMATION`: PayU is currently waiting for the merchant - /// system to receive (capture) the payment. This status is set if - /// auto-receive is disabled on the merchant system. - /// * `COMPLETED`: Payment has been accepted. PayU will pay out the funds - /// shortly. - /// * `CANCELED`: Payment has been cancelled and the buyer has not been - /// charged (no money was taken from buyer's account). - /// - /// > Too prevent sending wrong status from server to PayU this field - /// > remains String - status_code: String, - status_desc: Option, - code: Option, - severity: Option, - code_literal: Option, -} - -impl Status { - /// Check if http request was successful - /// - /// # Examples - /// - /// ``` - /// # use pay_u::Status; - /// let status: Status = serde_json::from_str("{\"statusCode\":\"SUCCESS\"}").unwrap(); - /// assert_eq!(status.is_success(), true); - /// ``` - pub fn is_success(&self) -> bool { - self.status_code.as_str() == SUCCESS - } - - /// Returns http status - /// - /// # Examples - /// - /// ``` - /// # use pay_u::Status; - /// let status: Status = serde_json::from_str("{\"statusCode\":\"SUCCESS\"}").unwrap(); - /// assert_eq!(status.status_code(), "SUCCESS"); - /// ``` - pub fn status_code(&self) -> &str { - &self.status_code - } - - pub fn status_desc(&self) -> Option<&str> { - self.status_desc.as_deref() - } - - pub fn code(&self) -> Option<&str> { - self.code.as_deref() - } - - pub fn severity(&self) -> Option<&str> { - self.severity.as_deref() - } - - pub fn code_literal(&self) -> Option<&CodeLiteral> { - self.code_literal.as_ref() - } -} - -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum StatusCode { - ErrorValueMissing, - OpenpayuBusinessError, - OpenpayuErrorValueInvalid, - OpenpayuErrorInternal, -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum CodeLiteral { - /// Request lacks "refund" object. - MissingRefundSection, - - /// Transaction has not been finalized - TransNotEnded, - - /// Lack of funds in account - NoBalance, - - /// Refund amount exceeds transaction amount - AmountToBig, - - /// Refund value is too small - AmountToSmall, - - /// Refunds have been disabled - RefundDisabled, - - /// Too many refund attempts have been made - RefundToOften, - - /// Refund was already created - Paid, - - /// Unknown error - UnknownError, - - /// extRefundId was re-used and other params do not match the values - /// sent during the first call. - RefundIdempotencyMismatch, - - /// Shop billing has not yet been completed - TransBillingEntriesNotCompleted, - - /// The available time for refund has passed. - TransTooOld, - - /// Transaction amount that remains after refund creation will be too - /// small to make another refund. - RemainingTransAmountTooSmall, - - #[serde(other)] - /// Implementation changed - Unknown, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Prop { - pub name: String, - pub value: String, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Refund { - pub refund_id: String, - pub ext_refund_id: Option, - pub amount: String, - pub currency_code: String, - pub description: String, - pub creation_date_time: String, - pub status: String, - pub status_date_time: String, -} - -pub struct Client { - sandbox: bool, - merchant_pos_id: MerchantPosId, - client_id: ClientId, - client_secret: ClientSecret, - bearer: Option, - bearer_expires_at: chrono::DateTime, - #[cfg(feature = "single-client")] - client: Arc, -} - -impl Client { - /// Create new PayU client - pub fn new( - client_id: ClientId, - client_secret: ClientSecret, - merchant_pos_id: MerchantPosId, - ) -> Self { - #[cfg(feature = "single-client")] - { - Self { - bearer: None, - sandbox: false, - merchant_pos_id, - client_id, - client_secret, - bearer_expires_at: chrono::Utc::now(), - client: Arc::new(Self::build_client()), - } - } - #[cfg(not(feature = "single-client"))] - { - Self { - bearer: None, - sandbox: false, - merchant_pos_id, - client_id, - client_secret, - bearer_expires_at: chrono::Utc::now(), - } - } - } - - /// All operation will be performed in sandbox PayU environment - /// - /// Examples: - /// - /// ``` - /// # use pay_u::*; - /// - /// async fn test() { - /// let client_id = ClientId::new(std::env::var("PAYU_CLIENT_ID").unwrap()); - /// let client_secret = ClientSecret::new(std::env::var("PAYU_CLIENT_SECRET").unwrap()); - /// let merchant_id = std::env::var("PAYU_CLIENT_MERCHANT_ID").unwrap().parse::().map(MerchantPosId::from).unwrap(); - /// let mut client = Client::new(client_id, client_secret, merchant_id).sandbox(); - /// } - /// ``` - pub fn sandbox(mut self) -> Self { - self.sandbox = true; - self - } - - /// Set your own bearer key - /// - /// Examples: - /// - /// ``` - /// # use pay_u::*; - /// - /// async fn test() { - /// let client_id = ClientId::new(std::env::var("PAYU_CLIENT_ID").unwrap()); - /// let client_secret = ClientSecret::new(std::env::var("PAYU_CLIENT_SECRET").unwrap()); - /// let merchant_id = std::env::var("PAYU_CLIENT_MERCHANT_ID").unwrap().parse::().map(MerchantPosId::from).unwrap(); - /// let mut client = Client::new(client_id, client_secret, merchant_id).with_bearer("a89sdhas9d8yasd8", 9_999_999); - /// } - /// ``` - pub fn with_bearer>(mut self, bearer: Bearer, expires_in: i64) -> Self { - self.bearer = Some(bearer.into()); - self.bearer_expires_at = chrono::Utc::now() + chrono::Duration::seconds(expires_in); - self - } - - /// Create new order in PayU - /// - /// ### IMPORTANT: - /// Do not follow redirect for any reason. Location points to - /// payment page which is useless in this context - /// - /// ### IMPORTANT: - /// Do not use rustls. It'll freeze application! - /// - /// # Examples - /// - /// ```rust - /// # use pay_u::*; - /// async fn pay() { - /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) - /// .sandbox() - /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000); - /// let res = client - /// .create_order( - /// req::OrderCreate::build( - /// Buyer::new("john.doe@example.com", "654111654", "John", "Doe", "pl"), - /// "127.0.0.1", - /// "PLN", - /// "Some description" - /// ).expect("All fields must be valid") - /// .with_notify_url("https://your.eshop.com/notify") - /// .with_description("Replace description") - /// .with_products([ - /// Product::new("Wireless Mouse for Laptop", 15000, 1), - /// Product::new("HDMI cable", 6000, 1), - /// ].into_iter()), - /// ) - /// .await; - /// } - /// ``` - pub async fn create_order(&mut self, order: req::OrderCreate) -> Result { - self.authorize().await?; - if order.total_amount - != order - .products - .iter() - .fold(0, |memo, p| memo + (p.unit_price * p.quantity as i32)) - { - return Err(Error::IncorrectTotal); - } - - if order.buyer().is_none() { - return Err(Error::NoBuyer); - } - - if order.description().trim().is_empty() { - return Err(Error::NoDescription); - } - - let bearer = self.bearer.as_ref().cloned().unwrap_or_default(); - let path = format!("{}/orders", self.base_url()); - let client = get_client!(self); - let text = client - .post(path) - .bearer_auth(bearer) - .json(&order.with_merchant_pos_id(self.merchant_pos_id)) - .send() - .await? - .text() - .await?; - log::trace!("Response: {}", text); - let res: res::CreateOrder = serde_json::from_str(&text).map_err(|e| { - log::error!("{e:?}"); - Error::CreateOrder - })?; - if !res.status.is_success() { - let Status { - status_code, - status_desc, - code, - severity, - code_literal, - } = res.status; - return Err(Error::CreateFailed { - status_desc, - status_code, - code, - severity, - code_literal, - }); - } - Ok(res) - } - - /// The PayU system fully supports refunds for processed payments, the - /// balance of which is transferred directly to the buyer’s account. - /// - /// > For „PayU | Pay later” payment method funds are transferred to a - /// > credit provider. - /// - /// You can process refund requests as either full or partial. For partial - /// refunds, always specify the amount in the lowest unit of a given - /// currency, which must be the same currency as the initial order (np. for - /// Poland lowest currency unit is “grosz” so 10 pln should be given as - /// 1000). - /// - /// You can send several partial refund requests for each single order. The - /// total value of the requests must not exceed the order value. - /// - /// The payu system allows multiple partial refunds to be executed at the - /// same time. To do so, the extRefundId parameter must be sent in the - /// request. In situations where partial refunds will not be executed more - /// than once per second, the extRefundId parameter is not required. - /// - /// # Examples - /// - /// ``` - /// # use pay_u::*; - /// async fn partial_refund() { - /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) - /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) - /// .sandbox(); - /// let res = client - /// .refund( - /// OrderId::new("H9LL64F37H160126GUEST000P01"), - /// req::Refund::new("Refund", Some(1000)), - /// ) - /// .await; - /// } - /// async fn full_refund() { - /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) - /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) - /// .sandbox(); - /// let res = client - /// .refund( - /// OrderId::new("H9LL64F37H160126GUEST000P01"), - /// req::Refund::new("Refund", None), - /// ) - /// .await; - /// } - /// ``` - pub async fn refund( - &mut self, - order_id: OrderId, - refund: req::Refund, - ) -> Result { - #[derive(Serialize, Debug)] - #[serde(rename_all = "camelCase")] - struct RefundWrapper { - refund: req::Refund, - } - - self.authorize().await?; - if refund.description().trim().is_empty() { - return Err(Error::NoDescription); - } - - let bearer = self.bearer.as_ref().cloned().unwrap_or_default(); - let path = format!("{}/orders/{}/refunds", self.base_url(), order_id); - let client = get_client!(self); - let text = client - .post(path) - .bearer_auth(bearer) - .json(&RefundWrapper { refund }) - .send() - .await? - .text() - .await?; - log::trace!("Response: {}", text); - let res: res::RefundDetails = serde_json::from_str(&text).map_err(|e| { - log::error!("Invalid PayU response {e:?}"); - Error::Refund - })?; - if !res.status.is_success() { - let Status { - status_code, - status_desc, - code, - severity, - code_literal, - } = res.status; - return Err(Error::RefundFailed { - status_desc, - status_code, - code, - severity, - code_literal, - }); - } - Ok(res) - } - - /// Order details request. You may use it to completely remove `Order` - /// persistence and use extOrderId to connect your data with PayU data. - /// - /// # Examples - /// - /// ``` - /// # use pay_u::*; - /// async fn order_details() { - /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) - /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) - /// .sandbox(); - /// let res = client - /// .order_details(OrderId::new("H9LL64F37H160126GUEST000P01")) - /// .await; - /// } - /// ``` - pub async fn order_details(&mut self, order_id: OrderId) -> Result { - self.authorize().await?; - let bearer = self.bearer.as_ref().cloned().unwrap_or_default(); - let path = format!("{}/orders/{}", self.base_url(), order_id); - let client = get_client!(self); - let text = client - .get(path) - .bearer_auth(bearer) - .send() - .await? - .text() - .await?; - log::trace!("Response: {}", text); - dbg!(&text); - let mut res: OrdersInfo = serde_json::from_str(&text).map_err(|e| { - log::error!("{e:?}"); - dbg!(e); - Error::OrderDetails - })?; - if !res.status.is_success() { - let Status { - status_code, - status_desc, - code, - severity, - code_literal, - } = res.status; - return Err(Error::OrderDetailsFailed { - status_code, - status_desc, - code, - severity, - code_literal, - }); - } - Ok(res::OrderInfo { - order: if res.orders.is_empty() { - return Err(Error::NoOrderInDetails); - } else { - res.orders.remove(0) - }, - status: res.status, - properties: res.properties, - }) - } - - /// The transaction retrieve request message enables you to retrieve the - /// details of transactions created for an order. - /// - /// Using this endpoint is extremely useful if you would like to get bank - /// account details or card details. - /// - /// > Please note that although card details are available right after - /// > transaction has been processed, the bank details may be available - /// > either after few minutes or on the next business day, depending on the - /// > bank. - /// - /// # Examples - /// - /// ``` - /// # use pay_u::*; - /// async fn order_transactions() { - /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) - /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) - /// .sandbox(); - /// let res = client - /// .order_transactions(OrderId::new("H9LL64F37H160126GUEST000P01")) - /// .await; - /// } - /// ``` - pub async fn order_transactions(&mut self, order_id: OrderId) -> Result { - self.authorize().await?; - let bearer = self.bearer.as_ref().cloned().unwrap_or_default(); - let path = format!("{}/orders/{}/transactions", self.base_url(), order_id); - let client = get_client!(self); - let text = client - .get(path) - .bearer_auth(bearer) - .send() - .await? - .text() - .await?; - log::trace!("Response: {}", text); - dbg!(&text); - serde_json::from_str(&text).map_err(|e| { - log::error!("{e:?}"); - dbg!(e); - Error::OrderTransactions - }) - } - - /// The transaction retrieve request message enables you to retrieve the - /// details of transactions created for an order. - /// - /// Using this endpoint is extremely useful if you would like to get bank - /// account details or card details. - /// - /// > Please note that although card details are available right after - /// > transaction has been processed, the bank details may be available - /// > either after few minutes or on the next business day, depending on the - /// > bank. - /// - /// # Examples - /// - /// ``` - /// # use pay_u::*; - /// async fn order_transactions() { - /// let mut client = Client::new(ClientId::new("145227"), ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), MerchantPosId::new(300746)) - /// .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 2000) - /// .sandbox(); - /// let res = client - /// .order_transactions(OrderId::new("H9LL64F37H160126GUEST000P01")) - /// .await; - /// } - /// ``` - pub async fn order_refunds(&mut self, order_id: OrderId) -> Result> { - self.authorize().await?; - let bearer = self.bearer.as_ref().cloned().unwrap_or_default(); - let path = format!("{}/orders/{}/refunds", self.base_url(), order_id); - let client = get_client!(self); - let text = client - .get(path) - .bearer_auth(bearer) - .send() - .await? - .text() - .await?; - log::trace!("Response: {}", text); - let res::Refunds { refunds, .. } = serde_json::from_str(&text).map_err(|e| { - log::error!("{e:?}"); - Error::OrderRefunds - })?; - Ok(refunds) - } - - /// Get or refresh token - pub async fn authorize(&mut self) -> Result { - use chrono::{Duration, Utc}; - if Utc::now() - Duration::seconds(1) < self.bearer_expires_at { - return Ok(true); - } - #[derive(Deserialize)] - struct BearerResult { - access_token: String, - expires_in: i64, - } - - let client = get_client!(self); - let res = client.post(&format!( - "https://secure.payu.com/pl/standard/user/oauth/authorize?grant_type=client_credentials&client_id={}&client_secret={}", - self.client_id, - self.client_secret - )) - .send() - .await?; - let res = res.json::().await.map_err(|e| { - log::error!("{e}"); - Error::Unauthorized - })?; - log::trace!("Bearer is {}", res.access_token); - self.bearer_expires_at = Utc::now() + Duration::seconds(res.expires_in); - self.bearer = Some(res.access_token); - Ok(true) - } - - fn base_url(&self) -> &str { - if self.sandbox { - "https://secure.snd.payu.com/api/v2_1" - } else { - "https://secure.payu.com/api/v2_1" - } - } - - fn build_client() -> reqwest::Client { - reqwest::ClientBuilder::default() - .user_agent("curl/7.82.0") - .use_native_tls() - // Do not follow redirect! - .redirect(redirect::Policy::none()) - .connection_verbose(true) - .build() - .expect("Failed to create client") - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::res::CreateOrder; - - fn build_client() -> Client { - dotenv::dotenv().ok(); - Client::new( - ClientId::new("145227"), - ClientSecret::new("12f071174cb7eb79d4aac5bc2f07563f"), - MerchantPosId::new(300746), - ) - .sandbox() - .with_bearer("d9a4536e-62ba-4f60-8017-6053211d3f47", 999999) - } - - async fn perform_create_order(client: &mut Client) -> Result { - client - .create_order( - req::OrderCreate::build( - Buyer::new("john.doe@example.com", "654111654", "John", "Doe", "pl"), - "127.0.0.1", - "PLN", - "Desc", - )? - .with_notify_url("https://your.eshop.com/notify") - .with_description("RTV market") - .with_products( - [ - Product::new("Wireless Mouse for Laptop", 15000, 1), - Product::new("HDMI cable", 6000, 1), - ] - .into_iter(), - ), - ) - .await - } - - #[tokio::test] - async fn create_order() { - let mut client = build_client(); - let res = perform_create_order(&mut client).await; - - if res.is_err() { - eprintln!("create_order res is {res:?}"); - } - assert!(res.is_ok()); - } - - #[tokio::test] - async fn partial_refund() { - let mut client = build_client(); - let CreateOrder { order_id, .. } = perform_create_order(&mut client) - .await - .expect("Failed to create"); - let res = client - .refund(order_id, req::Refund::new("Refund", Some(10))) - .await; - - if res.is_err() { - eprintln!("partial_refund res is {res:?}"); - } - assert!(matches!(res, Err(Error::RefundFailed { .. }))); - } - - #[tokio::test] - async fn full_refund() { - let mut client = build_client(); - let CreateOrder { order_id, .. } = perform_create_order(&mut client) - .await - .expect("Failed to create"); - let res = client - .refund(order_id, req::Refund::new("Refund", None)) - .await; - - if res.is_err() { - eprintln!("full_refund res is {res:?}"); - } - assert!(matches!(res, Err(Error::RefundFailed { .. }))); - } - - #[tokio::test] - async fn order_details() { - let mut client = build_client(); - let CreateOrder { order_id, .. } = perform_create_order(&mut client) - .await - .expect("Failed to create"); - let res = client.order_details(order_id).await; - - if res.is_err() { - eprintln!("order_details res is {res:?}"); - } - assert!(matches!(res, Ok(res::OrderInfo { .. }))); - } - - #[tokio::test] - async fn order_transactions() { - let mut client = build_client(); - let CreateOrder { order_id, .. } = perform_create_order(&mut client) - .await - .expect("Failed to create"); - let res = client.order_transactions(order_id).await; - if res.is_err() { - eprintln!("order_transactions res is {res:?}"); - } - assert!(matches!(res, Err(Error::OrderTransactions))); - } - - #[test] - fn check_accepted_refund_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/accepted_refund.json" - )); - assert!(res.is_ok()); - } - #[test] - fn check_cancel_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/cancel.json" - )); - assert!(res.is_ok()); - } - #[test] - fn check_completed_cart_token_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/completed_cart_token.json" - )); - assert!(res.is_ok()); - } - #[test] - fn check_completed_installments_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/completed_installments.json" - )); - assert!(res.is_ok()); - } - #[test] - fn check_completed_pbl_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/completed_pbl.json" - )); - assert!(res.is_ok()); - } - #[test] - fn check_refund_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/refund.json" - )); - assert!(res.is_ok()); - } - #[test] - fn check_rejection_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/rejection.json" - )); - assert!(res.is_ok()); - } - #[test] - fn check_custom_literal_json() { - let res = serde_json::from_str::(include_str!( - "../tests/responses/custom_code_literal.json" - )); - assert!(res.is_ok()); - } -} diff --git a/pay_u/src/notify.rs b/pay_u/src/notify.rs deleted file mode 100644 index cc118aa..0000000 --- a/pay_u/src/notify.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! Notification objects. Those objects will be send on notify_url if was given. -//! -//! To enable notifications for a specific payment, specify the notifyUrl -//! parameter in the payment form. Each payment can receive a different URL to -//! which notifications will be sent. -//! -//! Every notification is sent asynchronously. After your system receives a -//! notification with the status COMPLETED, instruct it to ignore any further -//! notifications. -//! -//! After sending a notification, PayU system requires a 200 HTTP status code in -//! response. If it receives a different status code, it will resend the -//! notification. Your system should also take account of situations where a -//! notification is sent several times with the same status. For each repeated -//! notification, response with code 200 should be sent as well. -//! -//! To ensure trusted communication between PayU and your shop, you must verify -//! the signature value available in the OpenPayu-Signature header each time -//! your system receives any notification from a PayU server. Refer to the -//! Verification of notifications signature for more information. -//! -//! Notifications are sent for orders in the following statuses: PENDING, -//! WAITING_FOR_CONFIRMATION, COMPLETED, CANCELED. -//! -//! Note: if you filter IP addresses, remember to allow IPs used by PayU to send -//! the notifications. These are: -//! -//! ### PRODUCTION -//! -//! > 185.68.12.10, 185.68.12.11, 185.68.12.12, 185.68.12.26, 185.68.12.27, -//! > 185.68.12.28 -//! -//! ### SANDBOX -//! -//! > 185.68.14.10, 185.68.14.11, 185.68.14.12, 185.68.14.26, 185.68.14.27, -//! > 185.68.14.28 -//! -//! ## Payment lifecycle -//! -//! You can configure a separate auto-receive / automatic collection parameter -//! for each payment method via the Management Panel. -//! -//! By default, auto-receive is enabled. The basic payment sequence is as -//! follows: -//! -//! 1) Each successfully authorized payment for an order is captured. -//! 2) The buyer is charged with the order amount. -//! 3) The shop balance is increased by the order amount. -//! 4) PayU calculates its commission to the order. -//! -//! If the auto-receive is turned off, you should capture each order using a PUT -//! method (Order capture) or cancel using DELETE method (Cancellation). -//! -//! If no such action is taken the order is auto-canceled. Automatic -//! cancellation occurs after a number of days indicated for the payment method. -//! -//! - -use serde::Deserialize; - -use super::deserialize; -use crate::OrderId; - -/// Payment notification object received on notify_url -/// -/// See [crate::req::CreateOrder::notify_url] -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct StatusUpdate { - /// Section containing order object - pub order: Order, - /// Moment of accepting the transaction and adding funds from the - /// transaction to the Shop balance. Format: "%Y-%M-%DT%h:%m:%s%z." - /// Example: "2020-06-09T17:52:04.644+02:00". If the millisecond counter - /// is "000" then milliseconds are not sent and the format changes to: - /// "%Y-%M-%DT%h:%m:%s". Is present only for the status "COMPLETED". - pub local_receipt_date_time: Option, - /// Array of objects related to transaction identification. In case of - /// statuses: - /// * `"WAITING_FOR_CONFIRMATION"` and `"COMPLETED"` - Contains one element - /// with two parameters: name and value, - /// * `"PENDING"` - may contain object with aforementioned parameters or it - /// can be empty. - /// - /// Also properties `name` - /// - /// Static value. The payment identifier, displayed on transaction - /// statements as "Trans ID" and within the transaction search option in - /// the Management Panel. - /// - /// Also properties `value` - /// - /// Transaction ID in PayU system (data type - string). - pub properties: Option>, -} - -impl StatusUpdate { - pub fn status(&self) -> super::PaymentStatus { - self.order.status - } - - /// See [crate::req::OrderCreate::ext_order_id] - pub fn ext_order_id(&self) -> &str { - self.order.ext_order_id.as_deref().unwrap_or_default() - } - - pub fn has_ext_order_id(&self) -> bool { - self.order.ext_order_id.is_some() - } -} - -/// Refund notification object -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct RefundUpdate { - pub ext_order_id: String, - pub order_id: OrderId, - pub refund: Refund, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Refund { - pub refund_id: String, - pub amount: String, - pub currency_code: String, - pub status: super::RefundStatus, - pub status_date_time: String, - pub reason: String, - pub reason_description: String, - pub refund_date: String, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Order { - pub notify_url: Option, - /// Customer client IP address - pub customer_ip: String, - /// Secret pos ip. This is connected to PayU account - #[serde(deserialize_with = "deserialize::deserialize_i32_newtype")] - pub merchant_pos_id: super::MerchantPosId, - /// Transaction description - pub description: String, - /// 3 characters currency identifier, ex. PLN - pub currency_code: String, - /// Total price of the order in pennies (e.g. 1000 is 10.00 EUR). - /// Applies also to currencies without subunits (e.g. 1000 is 10 - /// HUF). - #[serde(deserialize_with = "deserialize::deserialize_i32")] - pub total_amount: super::Price, - /// @see [crate::Buyer] - pub buyer: Option, - /// List of products - pub products: Vec, - #[serde(skip_serializing)] - pub order_create_date: Option, - pub pay_method: Option, - pub status: super::PaymentStatus, - pub ext_order_id: Option, -} diff --git a/pay_u/src/req.rs b/pay_u/src/req.rs deleted file mode 100644 index d05e05f..0000000 --- a/pay_u/src/req.rs +++ /dev/null @@ -1,381 +0,0 @@ -//! Objects used to send requests to PayU - -use serde::{Deserialize, Serialize}; - -use crate::credit::Credit; - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Refund { - pub(crate) description: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) amount: Option, -} - -impl Refund { - pub fn new(description: Description, amount: Option) -> Self - where - Description: Into, - { - Self { - description: description.into(), - amount, - } - } - - pub fn description(&self) -> &str { - &self.description - } - - pub fn amount(&self) -> Option { - self.amount - } -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct OrderCreate { - /// ID of an order used in merchant system. Order identifier assigned by - /// the merchant. It enables merchants to find a specific order - /// in their system. This value must be unique within a single - /// POS. - pub(crate) ext_order_id: Option, - /// URL to which web hook will be send. It's important to return 200 to - /// all notifications. - /// - /// All notifications are send as POST with JSON payload - /// - /// Notifications are sent immediately after a payment status changes. - /// If the notification is not received by the Shop application, - /// it will be sent again in accordance with the table below: - /// - /// | Attempt | Time | - /// |---------|------| - /// | 1 | immediately | - /// | 2 | 1 minute | - /// | 3 | 2 minutes | - /// | 4 | 5 minutes | - /// | 5 | 10 minutes | - /// | 6 | 30 minutes | - /// | 7 | 1 hour | - /// | 8 | 2 hours | - /// | 9 | 3 hours | - /// | 10| 6 hours | - /// | 11| 9 hours | - /// | 12| 12 hours | - /// | 13| 15 hours | - /// | 14| 18 hours | - /// | 15| 21 hours | - /// | 16| 24 hours | - /// | 17| 36 hours | - /// | 18| 48 hours | - /// | 19| 60 hours | - /// | 20| 72 hours | - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) notify_url: Option, - /// Address for redirecting the customer after payment is commenced. If - /// the payment has not been authorized, error=501 parameter - /// will be added. Please note that no decision regarding - /// payment status should be made depending on the presence or - /// lack of this parameter (to get payment status, wait for - /// notification or retrieve order details). - /// - /// IMPORTANT: the address must be compliant with the structure below: - /// - /// - /// Please keep in mind: - /// * accepted schemas are http and https, - /// * such elements as port, path, query and fragment are optional, - /// * query values must be encoded. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) continue_url: Option, - /// Payer’s IP address, e.g. 123.123.123.123. Note: 0.0.0.0 is not - /// accepted. - pub(crate) customer_ip: String, - /// Secret pos ip. This is connected to PayU account - #[serde( - serialize_with = "crate::serialize::serialize_newtype", - deserialize_with = "crate::deserialize::deserialize_i32_newtype" - )] - pub(crate) merchant_pos_id: super::MerchantPosId, - /// Transaction description - pub(crate) description: String, - /// 3 characters currency identifier, ex. PLN - pub(crate) currency_code: String, - /// Total price of the order in pennies (e.g. 1000 is 10.00 EUR). - /// Applies also to currencies without subunits (e.g. 1000 is 10 - /// HUF). - #[serde( - serialize_with = "crate::serialize::serialize_i32", - deserialize_with = "crate::deserialize::deserialize_i32" - )] - pub(crate) total_amount: super::Price, - /// @see [crate::Buyer] - pub(crate) buyer: Option, - /// List of products - pub(crate) products: Vec, - #[serde(skip_serializing)] - pub(crate) order_create_date: Option, - /// Duration for the validity of an order (in seconds), during which - /// time payment must be made. Default value 86400. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) validity_time: Option, - /// Additional description of the order. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) additional_description: Option, - /// Text visible on the PayU payment page (max. 80 chars). - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) visible_description: Option, - /// Payment recipient name followed by payment description (order ID, - /// ticket number etc) visible on card statement (max. 22 - /// chars). The name should be easy to recognize by the - /// cardholder (e.g "shop.com 124343"). If field - /// is not provided, static name configured by PayU will be used. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) statement_description: Option, - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub(crate) muct: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) credit: Option, -} - -impl OrderCreate { - pub fn build( - buyer: super::Buyer, - customer_ip: CustomerIp, - currency: Currency, - description: Description, - ) -> super::Result - where - CustomerIp: Into, - Currency: Into, - Description: Into, - { - let customer_ip = customer_ip.into(); - if &customer_ip == "0.0.0.0" { - return Err(super::Error::CustomerIp); - } - Ok(Self { - ext_order_id: None, - notify_url: None, - continue_url: None, - customer_ip, - merchant_pos_id: 0.into(), - description: description.into(), - currency_code: currency.into(), - total_amount: 0, - buyer: Some(buyer), - products: Vec::new(), - order_create_date: None, - validity_time: None, - additional_description: None, - visible_description: None, - statement_description: None, - muct: None, - credit: None, - }) - } - - /// ID of an order used in merchant system. Order identifier assigned by - /// the merchant. It enables merchants to find a specific order - /// in their system. This value must be unique within a single - /// POS. - pub fn with_ext_order_id>(mut self, ext_order_id: S) -> Self { - self.ext_order_id = Some(ext_order_id.into()); - self - } - - /// Duration for the validity of an order (in seconds), during which - /// time payment must be made. Default value 86400. - pub fn with_validity_time(mut self, validity_time: u16) -> Self { - self.validity_time = Some(validity_time); - self - } - - pub fn with_multi_use_token( - mut self, - recurring: super::muct::Recurring, - card_on_file: super::muct::CardOnFile, - ) -> Self { - self.muct = Some(super::muct::MultiUseCartToken { - recurring, - card_on_file, - }); - self - } - - pub fn with_products(mut self, products: Products) -> Self - where - Products: Iterator, - { - self.products.extend(products); - self.total_amount = self - .products - .iter() - .fold(0, |agg, p| agg + (p.quantity as i32 * p.unit_price as i32)); - self - } - - pub fn with_product(mut self, product: super::Product) -> Self { - self.products.push(product); - self.total_amount = self - .products - .iter() - .fold(0, |agg, p| agg + (p.quantity as i32 * p.unit_price as i32)); - self - } - - /// Description of the an order. - /// - /// > This method will override initial description! - pub fn with_description(mut self, desc: Description) -> Self - where - Description: Into, - { - self.description = String::from(desc.into().trim()); - self - } - - /// Additional description of the order. - pub fn with_additional_description>( - mut self, - additional_description: S, - ) -> Self { - self.additional_description = Some(additional_description.into()); - self - } - - /// Text visible on the PayU payment page (max. 80 chars). - pub fn with_visible_description(mut self, visible_description: &str) -> Self { - let visible_description = if visible_description.len() > 60 { - &visible_description[..60] - } else { - visible_description - }; - self.visible_description = Some(String::from(visible_description)); - self - } - - /// Payment recipient name followed by payment description (order ID, - /// ticket number etc) visible on card statement (max. 22 - /// chars). The name should be easy to recognize by the - /// cardholder (e.g "shop.com 124343"). If field - /// is not provided, static name configured by PayU will be used. - pub fn with_statement_description(mut self, desc: Description) -> Self - where - Description: Into, - { - self.statement_description = Some(String::from(desc.into().trim())); - self - } - - /// Add url to which PayU will be able to send http request with payment - /// status updates - /// - /// All requests from PayU should receive 200 response! - /// - /// See more [crate::res::Order::notify_url] - pub fn with_notify_url(mut self, notify_url: NotifyUrl) -> Self - where - NotifyUrl: Into, - { - self.notify_url = Some(notify_url.into()); - self - } - - /// Address for redirecting the customer after payment is commenced. If - /// the payment has not been authorized, error=501 parameter - /// will be added. Please note that no decision regarding - /// payment status should be made depending on the presence or - /// lack of this parameter (to get payment status, wait for - /// notification or retrieve order details). - pub fn with_continue_url(mut self, continue_url: ContinueUrl) -> Self - where - ContinueUrl: Into, - { - self.continue_url = Some(continue_url.into()); - self - } - - /// Section containing credit data. This information is not required, - /// but it is strongly recommended to include it. Otherwise the - /// buyer will be prompted to provide missing data on provider - /// page when payment by Installments or Pay later. - pub fn with_credit(mut self, credit: Credit) -> Self { - self.credit = Some(credit); - self - } - - /// URL to which web hook will be send. It's important to return 200 to - /// all notifications. - /// - /// All notifications are send as POST with JSON payload - /// - /// Notifications are sent immediately after a payment status changes. - /// If the notification is not received by the Shop application, - /// it will be sent again in accordance with the table below: - /// - /// | Attempt | Time | - /// |---------|------| - /// | 1 | immediately | - /// | 2 | 1 minute | - /// | 3 | 2 minutes | - /// | 4 | 5 minutes | - /// | 5 | 10 minutes | - /// | 6 | 30 minutes | - /// | 7 | 1 hour | - /// | 8 | 2 hours | - /// | 9 | 3 hours | - /// | 10| 6 hours | - /// | 11| 9 hours | - /// | 12| 12 hours | - /// | 13| 15 hours | - /// | 14| 18 hours | - /// | 15| 21 hours | - /// | 16| 24 hours | - /// | 17| 36 hours | - /// | 18| 48 hours | - /// | 19| 60 hours | - /// | 20| 72 hours | - pub fn notify_url(&self) -> &Option { - &self.notify_url - } - - /// Customer IP address from http request received from client - pub fn customer_ip(&self) -> &String { - &self.customer_ip - } - - pub fn merchant_pos_id(&self) -> super::MerchantPosId { - self.merchant_pos_id - } - - pub fn description(&self) -> &String { - &self.description - } - - pub fn currency_code(&self) -> &String { - &self.currency_code - } - - pub fn total_amount(&self) -> &super::Price { - &self.total_amount - } - - pub fn buyer(&self) -> &Option { - &self.buyer - } - - pub fn products(&self) -> &[super::Product] { - &self.products - } - - pub fn order_create_date(&self) -> &Option { - &self.order_create_date - } - - pub(crate) fn with_merchant_pos_id(mut self, merchant_pos_id: super::MerchantPosId) -> Self { - self.merchant_pos_id = merchant_pos_id; - self - } -} diff --git a/pay_u/src/res.rs b/pay_u/src/res.rs deleted file mode 100644 index 1fcf1d2..0000000 --- a/pay_u/src/res.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::{OrderId, Refund, Status}; - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct CreateOrder { - /// Http status as a text - pub status: Status, - /// Client should be redirected to this URI - pub redirect_uri: String, - /// This should be connected to your own order - pub order_id: OrderId, - /// This is YOUR_EXT_ORDER_ID - pub ext_order_id: Option, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct RefundDetails { - pub order_id: Option, - pub refund: Option, - pub status: Status, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Refunds { - pub refunds: Vec, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct TransactionPayMethod { - pub value: String, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum CardProfile { - Consumer, - Business, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum CardClassification { - Debit, - Credit, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct TransactionCartData { - /// // "543402******4014", - pub card_number_masked: String, - /// MC (MasterCard/Maestro), VS (Visa) - /// Example; "MC" - pub card_scheme: String, - pub card_profile: CardProfile, - pub card_classification: CardClassification, - /// Example: "000" - pub card_response_code: String, - /// Example: "000 - OK" - pub card_response_code_desc: String, - /// Example: "5" - pub card_eci_code: String, - /// Example: "AY", - pub card3ds_status: String, - /// Example: "PL", - pub card_bin_country: String, - /// Example: "MCC0111LL1121" - pub first_transaction_id: String, -} - -/// > Installment proposal on the Sandbox environment is not related to the -/// > order amount and always returns data for 480 PLN. -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct TransactionCardInstallmentProposal { - /// Example: "5aff3ba8-0c37-4da1-ba4a-4ff24bcc2eed" - pub proposal_id: String, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct TransactionCart { - pub cart_data: TransactionCartData, - pub card_installment_proposal: TransactionCardInstallmentProposal, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - pub pay_method: TransactionPayMethod, - pub payment_flow: String, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Transactions { - pub transactions: Vec, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Order { - /// Example: "{orderId}", - pub order_id: super::OrderId, - /// Example: "358766", - pub ext_order_id: Option, - /// Example: "2014-10-27T14:58:17.443+01:00", - pub order_create_date: String, - /// Example: "http://localhost/OrderNotify/", - pub notify_url: Option, - /// Example: "127.0.0.1", - pub customer_ip: String, - /// Example: "145227", - pub merchant_pos_id: String, - /// Example: "New order", - pub description: String, - /// Example: "PLN", - pub currency_code: String, - /// Example: "3200", - pub total_amount: String, - /// Example: "NEW", - pub status: String, - /// Example: `[{"name":"Product1","unitPrice":"1000","quantity":"1"}]` - pub products: Vec, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct OrdersInfo { - pub orders: Vec, - pub status: Status, - pub properties: Option>, -} - -#[derive(serde::Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct OrderInfo { - pub order: Order, - pub status: Status, - pub properties: Option>, -} diff --git a/pay_u/src/serialize.rs b/pay_u/src/serialize.rs deleted file mode 100644 index 54682ad..0000000 --- a/pay_u/src/serialize.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::fmt::Display; - -pub(crate) fn serialize_i32(v: &i32, ser: S) -> std::result::Result -where - S: serde::Serializer, -{ - ser.serialize_str(&format!("{v}")) -} - -pub(crate) fn serialize_newtype( - v: &N, - ser: S, -) -> std::result::Result -where - S: serde::Serializer, -{ - ser.serialize_str(&format!("{v}")) -} - -pub(crate) fn serialize_u32(v: &u32, ser: S) -> std::result::Result -where - S: serde::Serializer, -{ - ser.serialize_str(&format!("{v}")) -} diff --git a/pay_u/tests/responses/accepted_refund.json b/pay_u/tests/responses/accepted_refund.json deleted file mode 100644 index 5ba37db..0000000 --- a/pay_u/tests/responses/accepted_refund.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "orderId": "ZXWZ53KQQM200702GUEST000P01", - "refund": { - "refundId": "5000009987", - "extRefundId": "20200702091903", - "amount": "21000", - "currencyCode": "PLN", - "description": "5000009987 Refund Accepted", - "creationDateTime": "2020-07-02T09:19:03.896+02:00", - "status": "PENDING", - "statusDateTime": "2020-07-02T09:19:04.013+02:00" - }, - "status": { - "statusCode": "SUCCESS", - "statusDesc": "Refund queued for processing" - } -} diff --git a/pay_u/tests/responses/cancel.json b/pay_u/tests/responses/cancel.json deleted file mode 100644 index 1fae831..0000000 --- a/pay_u/tests/responses/cancel.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "order": { - "orderId": "LDLW5N7MF4140324GUEST000P01", - "extOrderId": "Order id in your shop", - "orderCreateDate": "2012-12-31T12:00:00", - "notifyUrl": "http://tempuri.org/notify", - "customerIp": "127.0.0.1", - "merchantPosId": "98374", - "description": "My order description", - "currencyCode": "PLN", - "totalAmount": "200", - "products": [ - { - "name": "Product 1", - "unitPrice": "200", - "quantity": "1" - } - ], - "status": "CANCELED" - } -} diff --git a/pay_u/tests/responses/completed_cart_token.json b/pay_u/tests/responses/completed_cart_token.json deleted file mode 100644 index d1407dd..0000000 --- a/pay_u/tests/responses/completed_cart_token.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "order": { - "orderId": "LDLW5N7MF4140324GUEST000P01", - "extOrderId": "Order id in your shop", - "orderCreateDate": "2012-12-31T12:00:00", - "notifyUrl": "http://tempuri.org/notify", - "customerIp": "127.0.0.1", - "merchantPosId": "238684", - "description": "My order description", - "currencyCode": "PLN", - "totalAmount": "200", - "buyer": { - "email": "john.doe@example.org", - "phone": "111111111", - "firstName": "John", - "lastName": "Doe", - "language": "en" - }, - "payMethod": { - "type": "CARD_TOKEN" - }, - "products": [ - { - "name": "Product 1", - "unitPrice": "200", - "quantity": "1" - } - ], - "status": "COMPLETED" - }, - "localReceiptDateTime": "2016-03-02T12:58:14.828+01:00", - "properties": [ - { - "name": "PAYMENT_ID", - "value": "151471228" - } - ] -} diff --git a/pay_u/tests/responses/completed_installments.json b/pay_u/tests/responses/completed_installments.json deleted file mode 100644 index 3ccf59d..0000000 --- a/pay_u/tests/responses/completed_installments.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "order": { - "orderId": "LDLW5N7MF4140324GUEST000P01", - "extOrderId": "Order id in your shop", - "orderCreateDate": "2012-12-31T12:00:00", - "notifyUrl": "http://tempuri.org/notify", - "customerIp": "127.0.0.1", - "merchantPosId": "8236", - "description": "My order description", - "currencyCode": "PLN", - "totalAmount": "200", - "buyer": { - "email": "john.doe@example.org", - "phone": "111111111", - "firstName": "John", - "lastName": "Doe", - "language": "en" - }, - "payMethod": { - "type": "INSTALLMENTS" - }, - "products": [ - { - "name": "Product 1", - "unitPrice": "200", - "quantity": "1" - } - ], - "status": "COMPLETED" - }, - "localReceiptDateTime": "2016-03-02T12:58:14.828+01:00", - "properties": [ - { - "name": "PAYMENT_ID", - "value": "151471228" - } - ] -} diff --git a/pay_u/tests/responses/completed_pbl.json b/pay_u/tests/responses/completed_pbl.json deleted file mode 100644 index 2f4e329..0000000 --- a/pay_u/tests/responses/completed_pbl.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "order": { - "orderId": "LDLW5N7MF4140324GUEST000P01", - "extOrderId": "Order id in your shop", - "orderCreateDate": "2012-12-31T12:00:00", - "notifyUrl": "http://tempuri.org/notify", - "customerIp": "127.0.0.1", - "merchantPosId": "928374", - "description": "My order description", - "currencyCode": "PLN", - "totalAmount": "200", - "buyer": { - "email": "john.doe@example.org", - "phone": "111111111", - "firstName": "John", - "lastName": "Doe", - "language": "en" - }, - "payMethod": { - "type": "PBL" - }, - "products": [ - { - "name": "Product 1", - "unitPrice": "200", - "quantity": "1" - } - ], - "status": "COMPLETED" - }, - "localReceiptDateTime": "2016-03-02T12:58:14.828+01:00", - "properties": [ - { - "name": "PAYMENT_ID", - "value": "151471228" - } - ] -} diff --git a/pay_u/tests/responses/custom_code_literal.json b/pay_u/tests/responses/custom_code_literal.json deleted file mode 100644 index 90ada03..0000000 --- a/pay_u/tests/responses/custom_code_literal.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "status": { - "statusCode": "ERROR_VALUE_MISSING", - "severity": "ERROR", - "code": "8300", - "codeLiteral": "FOO_BAR", - "statusDesc": "Implementation changed" - } -} diff --git a/pay_u/tests/responses/refund.json b/pay_u/tests/responses/refund.json deleted file mode 100644 index 239e375..0000000 --- a/pay_u/tests/responses/refund.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "orderId": "2DVZMPMFPN140219GUEST000P01", - "extOrderId": "Order id in your shop", - "refund": { - "refundId": "912128", - "amount": "15516", - "currencyCode": "PLN", - "status": "FINALIZED", - "statusDateTime": "1395677071799", - "reason": "refund", - "reasonDescription": "on customer’s request", - "refundDate": "1395677071799" - } -} diff --git a/pay_u/tests/responses/rejection.json b/pay_u/tests/responses/rejection.json deleted file mode 100644 index bc78da1..0000000 --- a/pay_u/tests/responses/rejection.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "status": { - "statusCode": "ERROR_VALUE_MISSING", - "severity": "ERROR", - "code": "8300", - "codeLiteral": "MISSING_REFUND_SECTION", - "statusDesc": "Missing required field" - } -}