From 86191f455ecdba24af81c90bfdd30f44441ecfbd Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Mon, 8 Feb 2021 09:48:41 +0100 Subject: [PATCH] use serde_with --- Cargo.toml | 1 + src/common.rs | 27 +++------ src/countries.rs | 4 +- src/errors.rs | 7 +-- src/invoice.rs | 141 ++++++++++------------------------------------- src/lib.rs | 34 +++++------- src/orders.rs | 81 +++++++-------------------- 7 files changed, 77 insertions(+), 218 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a503a85..2639b89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ edition = "2018" reqwest = { version = "0.11.0", features = ["json"] } serde = { version = "1.0.118", features = ["derive"] } serde_json = "1.0.61" +serde_with = "1.6.2" chrono = { version = "0.4.19", features = ["serde"] } jsonwebtoken = "7.2.0" base64 = "0.13.0" diff --git a/src/common.rs b/src/common.rs index 36f54ba..72b92d5 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,8 +1,9 @@ //! Common paypal object definitions used by 2 or more APIs -use serde::{Deserialize, Serialize}; -use std::str::FromStr; use crate::errors::InvalidCurrencyError; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +use std::str::FromStr; /// The phone type. /// @@ -19,53 +20,43 @@ pub enum PhoneType { } /// The non-portable additional address details +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct AddressDetails { /// The street number. - #[serde(skip_serializing_if = "Option::is_none")] pub street_number: Option, /// The street name. Just Drury in Drury Lane. - #[serde(skip_serializing_if = "Option::is_none")] pub street_name: Option, /// The street type. For example, avenue, boulevard, road, or expressway. - #[serde(skip_serializing_if = "Option::is_none")] pub street_type: Option, /// The delivery service. Post office box, bag number, or post office name. - #[serde(skip_serializing_if = "Option::is_none")] pub delivery_service: Option, /// A named locations that represents the premise. Usually a building name or number or collection of buildings with a common name or number. For example, Craven House. - #[serde(skip_serializing_if = "Option::is_none")] pub building_name: Option, /// The first-order entity below a named building or location that represents the sub-premise. /// Usually a single building within a collection of buildings with a common name. Can be a flat, story, floor, room, or apartment. - #[serde(skip_serializing_if = "Option::is_none")] pub sub_building: Option, } /// The address of the payer. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Address { /// The first line of the address. For example, number or street. For example, 173 Drury Lane. /// Required for data entry and compliance and risk checks. Must contain the full address. - #[serde(skip_serializing_if = "Option::is_none")] pub address_line_1: Option, /// The second line of the address. For example, suite or apartment number. - #[serde(skip_serializing_if = "Option::is_none")] pub address_line_2: Option, /// A city, town, or village. Smaller than admin_area_level_1. - #[serde(skip_serializing_if = "Option::is_none")] pub admin_area_2: Option, /// The highest level sub-division in a country, which is usually a province, state, or ISO-3166-2 subdivision. /// Format for postal delivery. For example, CA and not California. - #[serde(skip_serializing_if = "Option::is_none")] pub admin_area_1: Option, /// The postal code, which is the zip code or equivalent. Typically required for countries with a postal code or an equivalent. - #[serde(skip_serializing_if = "Option::is_none")] pub postal_code: Option, /// The two-character [ISO 3166-1](https://developer.paypal.com/docs/api/reference/country-codes/) code that identifies the country or region. pub country_code: String, /// The non-portable additional address details that are sometimes needed for compliance, risk, or other scenarios where fine-grain address information might be needed. - #[serde(skip_serializing_if = "Option::is_none")] pub address_details: Option, } @@ -97,6 +88,7 @@ pub enum LinkMethod { } /// A HTOAES link +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct LinkDescription { /// The complete target URL. @@ -104,7 +96,6 @@ pub struct LinkDescription { /// The link relation type, which serves as an ID for a link that unambiguously describes the semantics of the link. pub rel: String, /// The HTTP method required to make the related call. - #[serde(skip_serializing_if = "Option::is_none")] pub method: Option, } @@ -153,7 +144,7 @@ pub enum Currency { GBP, /// Russian ruble RUB, - /// Singapore dollar + /// Singapore dollar SGD, /// Swedish krona SEK, @@ -162,7 +153,7 @@ pub enum Currency { /// Thai baht THB, /// United States dollar - USD + USD, } impl Default for Currency { @@ -206,7 +197,7 @@ impl FromStr for Currency { "CHF" => Ok(Self::CHF), "THB" => Ok(Self::THB), "USD" => Ok(Self::USD), - cur => Err(InvalidCurrencyError(cur.to_owned())) + cur => Err(InvalidCurrencyError(cur.to_owned())), } } } diff --git a/src/countries.rs b/src/countries.rs index 1cfe22e..8629d34 100644 --- a/src/countries.rs +++ b/src/countries.rs @@ -1,8 +1,8 @@ //! Generated using https://github.com/edg-l/payhelper +use crate::errors::InvalidCountryError; use serde::{Deserialize, Serialize}; use std::str::FromStr; -use crate::errors::InvalidCountryError; /// IS0-3166-1 country codes #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -629,7 +629,7 @@ impl FromStr for Country { "YE" => Ok(Self::YE), "ZM" => Ok(Self::ZM), "ZW" => Ok(Self::ZW), - country => Err(InvalidCountryError(country.to_owned())) + country => Err(InvalidCountryError(country.to_owned())), } } } diff --git a/src/errors.rs b/src/errors.rs index 9ef58cf..cfb1523 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -5,7 +5,6 @@ use std::collections::HashMap; use std::error::Error; use std::fmt; - /// A paypal api response error. #[derive(Debug, Serialize, Deserialize)] pub struct PaypalError { @@ -39,7 +38,7 @@ pub enum ResponseError { /// A paypal api error. ApiError(PaypalError), /// A http error. - HttpError(reqwest::Error) + HttpError(reqwest::Error), } impl fmt::Display for ResponseError { @@ -80,7 +79,7 @@ pub struct InvalidCurrencyError(pub String); impl fmt::Display for InvalidCurrencyError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} is not a valid currency", self.0) + write!(f, "{:?} is not a valid currency", self.0) } } @@ -92,7 +91,7 @@ pub struct InvalidCountryError(pub String); impl fmt::Display for InvalidCountryError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} is not a valid country", self.0) + write!(f, "{:?} is not a valid country", self.0) } } diff --git a/src/invoice.rs b/src/invoice.rs index 2639ba5..d0f7e11 100644 --- a/src/invoice.rs +++ b/src/invoice.rs @@ -7,11 +7,11 @@ //! Reference: https://developer.paypal.com/docs/api/invoicing/v2/ use crate::common::*; -use crate::countries::Country; +use crate::errors::{PaypalError, ResponseError}; use crate::HeaderParams; -use crate::errors::{ResponseError, PaypalError}; use bytes::Bytes; use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; use std::collections::HashMap; /// Paypal File reference @@ -76,64 +76,50 @@ pub enum FlowType { } /// Metadata about a resource +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct Metadata { /// The date and time when the resource was created - #[serde(skip_serializing_if = "Option::is_none")] pub create_time: Option>, /// The email address of the account that created the resource. - #[serde(skip_serializing_if = "Option::is_none")] pub created_by: Option, /// The date and time when the resource was last edited - #[serde(skip_serializing_if = "Option::is_none")] pub last_update_time: Option>, /// The email address of the account that last edited the resource. - #[serde(skip_serializing_if = "Option::is_none")] pub last_updated_by: Option>, /// The date and time when the resource was canceled - #[serde(skip_serializing_if = "Option::is_none")] pub cancel_time: Option>, /// The actor who canceled the resource. - #[serde(skip_serializing_if = "Option::is_none")] pub cancelled_by: Option>, /// The date and time when the resource was first sent - #[serde(skip_serializing_if = "Option::is_none")] pub first_sent_time: Option>, /// The date and time when the resource was last sent - #[serde(skip_serializing_if = "Option::is_none")] pub last_sent_time: Option>, /// The email address of the account that last sent the resource. - #[serde(skip_serializing_if = "Option::is_none")] pub last_sent_by: Option, /// The flow variation that created this invoice - #[serde(skip_serializing_if = "Option::is_none")] pub created_by_flow: Option, /// The URL for the invoice payer view hosted on paypal.com. - #[serde(skip_serializing_if = "Option::is_none")] pub recipient_view_url: Option, /// The URL for the invoice merchant view hosted on paypal.com - #[serde(skip_serializing_if = "Option::is_none")] pub invoicer_view_url: Option, } /// The details of the invoice. Includes the invoice number, date, payment terms, and audit metadata. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct InvoiceDetail { /// The reference data. Includes a post office (PO) number. - #[serde(skip_serializing_if = "Option::is_none")] pub reference: Option, /// The three-character ISO-4217 currency code that identifies the currency. pub currency_code: String, /// A note to the invoice recipient. Also appears on the invoice notification email. - #[serde(skip_serializing_if = "Option::is_none")] pub note: Option, /// The general terms of the invoice. Can include return or cancellation policy and other terms and conditions. - #[serde(skip_serializing_if = "Option::is_none")] pub terms_and_conditions: Option, /// A private bookkeeping memo for the user. pub memo: Option, /// An array of PayPal IDs for the files that are attached to an invoice. - #[serde(skip_serializing_if = "Option::is_none")] pub attachments: Option>, /// The invoice number. Default is the number that is auto-incremented number from the last number. pub invoice_number: String, @@ -146,35 +132,30 @@ pub struct InvoiceDetail { } /// A name to be used as recipient, etc. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct Name { /// The prefix, or title, to the party's name. - #[serde(skip_serializing_if = "Option::is_none")] pub prefix: Option, /// When the party is a person, the party's given, or first, name. - #[serde(skip_serializing_if = "Option::is_none")] pub given_name: Option, /// When the party is a person, the party's surname or family name. /// Also known as the last name. Required when the party is a person. /// Use also to store multiple surnames including the matronymic, or mother's, surname. - #[serde(skip_serializing_if = "Option::is_none")] pub surname: Option, /// When the party is a person, the party's middle name. Use also to store multiple middle names including the patronymic, or father's, middle name. - #[serde(skip_serializing_if = "Option::is_none")] pub middle_name: Option, /// The suffix for the party's name. - #[serde(skip_serializing_if = "Option::is_none")] pub suffix: Option, /// DEPRECATED. The party's alternate name. Can be a business name, nickname, /// or any other name that cannot be split into first, last name. Required when the party is a business. - #[serde(skip_serializing_if = "Option::is_none")] pub alternate_full_name: Option, /// When the party is a person, the party's full name. - #[serde(skip_serializing_if = "Option::is_none")] pub full_name: Option, } /// Phone information +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct PhoneDetail { /// The country calling code (CC), in its canonical international E.164 numbering plan format. @@ -182,92 +163,78 @@ pub struct PhoneDetail { /// The national number, in its canonical international E.164 numbering plan format. pub national_number: String, /// The extension number. - #[serde(skip_serializing_if = "Option::is_none")] pub extension_number: Option, /// The phone type. - #[serde(skip_serializing_if = "Option::is_none")] pub phone_type: Option, } /// The invoicer information. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct InvoicerInfo { /// Required. The business name of the party. pub business_name: String, /// The first and Last name of the recipient. - #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// The invoicer email address, which must be listed in the user's PayPal profile. /// If you omit this value, notifications are sent from and to the primary email address but do not appear on the invoice. - #[serde(skip_serializing_if = "Option::is_none")] pub email_address: Option, /// An array of invoicer's phone numbers. The invoicer can choose to hide the phone number on the invoice. - #[serde(skip_serializing_if = "Option::is_none")] pub phones: Option>, /// The invoicer's website. - #[serde(skip_serializing_if = "Option::is_none")] pub website: Option, /// The invoicer's tax ID. - #[serde(skip_serializing_if = "Option::is_none")] pub tax_id: Option, /// Any additional information. Includes business hours. - #[serde(skip_serializing_if = "Option::is_none")] pub additional_notes: Option, /// The full URL to an external logo image. The logo image must not be larger than 250 pixels wide by 90 pixels high. - #[serde(skip_serializing_if = "Option::is_none")] pub logo_url: Option, } /// Billing information +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct BillingInfo { /// Required. The business name of the party. pub business_name: String, /// The first and Last name of the recipient. - #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// The address of the recipient. - #[serde(skip_serializing_if = "Option::is_none")] pub address: Option
, /// The invoice recipient email address. If you omit this value, the invoice is payable and a notification email is not sent. - #[serde(skip_serializing_if = "Option::is_none")] pub email_address: Option, /// The invoice recipient's phone numbers. Extension number is not supported. - #[serde(skip_serializing_if = "Option::is_none")] pub phones: Option>, /// Any additional information about the recipient. Maximum length: 40. - #[serde(skip_serializing_if = "Option::is_none")] pub additional_info: Option, /// The language in which to show the invoice recipient's email message. Used only when the recipient does not have a PayPal account - #[serde(skip_serializing_if = "Option::is_none")] pub language: Option, } /// Contact information +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct ContactInformation { /// Required. The business name of the party. pub business_name: String, /// The first and Last name of the recipient. - #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// The address of the recipient. - #[serde(skip_serializing_if = "Option::is_none")] pub address: Option
, } /// Recipient information +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct RecipientInfo { /// The billing information for the invoice recipient. Includes name, address, email, phone, and language. - #[serde(skip_serializing_if = "Option::is_none")] pub billing_info: Option, /// The recipient's shipping information. Includes the user's contact information, which includes name and address. - #[serde(skip_serializing_if = "Option::is_none")] pub shipping_info: Option, } /// Tax information +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct Tax { /// The name of the tax applied on the invoice items. @@ -275,7 +242,6 @@ pub struct Tax { /// The tax rate. Value is from 0 to 100. Supports up to five decimal places. pub percent: String, /// The calculated tax amount. The tax amount is added to the item total. - #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option, } @@ -301,75 +267,64 @@ pub enum UnitOffMeasure { } /// Item information +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct Item { /// The ID of the invoice line item. /// Read only. - #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, /// The item name for the invoice line item. pub name: String, /// The item description for the invoice line item. - #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, /// The quantity of the item that the invoicer provides to the payer. Value is from -1000000 to 1000000. Supports up to five decimal places. pub quantity: String, /// The unit price of the item. This does not include tax and discount. Value is from -1000000 to 1000000. Supports up to two decimal places. pub unit_amount: Money, /// The tax associated with the item. The tax amount is added to the item total. Value is from 0 to 100. Supports up to five decimal places. - #[serde(skip_serializing_if = "Option::is_none")] pub tax: Option, /// The date when the item or service was provided, in Internet date and time format. - #[serde(skip_serializing_if = "Option::is_none")] pub item_date: Option>, /// Discount as a percent or amount at invoice level. The invoice discount amount is subtracted from the item total. - #[serde(skip_serializing_if = "Option::is_none")] pub discount: Option, /// The unit of measure for the invoiced item. For AMOUNT the unit_amount and quantity are not shown on the invoice. - #[serde(skip_serializing_if = "Option::is_none")] pub unit_of_measure: Option, } /// The partial payment details. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct PartialPayment { /// Indicates whether the invoice allows a partial payment. If false, the invoice must be paid in full. If true, the invoice allows partial payments. - #[serde(skip_serializing_if = "Option::is_none")] pub allow_partial_payment: Option, /// The minimum amount allowed for a partial payment. Valid only when allow_partial_payment is true. - #[serde(skip_serializing_if = "Option::is_none")] pub minimum_amount_due: Option, } /// The invoice configuration details. Includes partial payment, tip, and tax calculated after discount. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct Configuration { /// Indicates whether the tax is calculated before or after a discount. If false, the tax is calculated before a discount. If true, the tax is calculated after a discount. - #[serde(skip_serializing_if = "Option::is_none")] pub tax_calculated_after_discount: Option, /// Indicates whether the unit price includes tax. - #[serde(skip_serializing_if = "Option::is_none")] pub tax_inclusive: Option, /// Indicates whether the invoice enables the customer to enter a tip amount during payment. /// If true, the invoice shows a tip amount field so that the customer can enter a tip amount. If false, the invoice does not show a tip amount field. - #[serde(skip_serializing_if = "Option::is_none")] pub allow_tip: Option, /// The partial payment details. Includes the minimum amount that the invoicer wants the payer to pay. - #[serde(skip_serializing_if = "Option::is_none")] pub partial_payment: Option, /// The template ID. The template determines the layout of the invoice. Includes which fields to show and hide. Default: PayPal system template. - #[serde(skip_serializing_if = "Option::is_none")] pub template_id: Option, } /// The discount +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct AggregatedDiscount { /// The discount as a percent or amount at invoice level. The invoice discount amount is subtracted from the item total. - #[serde(skip_serializing_if = "Option::is_none")] pub invoice_discount: Option, /// The discount as a percent or amount at item level. The item discount amount is subtracted from each item amount. - #[serde(skip_serializing_if = "Option::is_none")] pub item_discount: Option, } @@ -377,44 +332,39 @@ pub struct AggregatedDiscount { #[derive(Debug, Default, Serialize, Deserialize)] pub struct ShippingCost { /// The shipping amount. Value is from 0 to 1000000. Supports up to two decimal places. - #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option, /// The tax associated with the shipping. - #[serde(skip_serializing_if = "Option::is_none")] pub tax: Option, } /// The custom amount to apply to an invoice +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct CustomAmount { /// The label to the custom amount of the invoice. pub label: String, /// The custom amount value. Value is from -1000000 to 1000000. Supports up to two decimal places. - #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option, } /// The breakdown of the amount. Breakdown provides details such as total item amount, total tax amount, custom amount, shipping and discounts, if any. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Breakdown { /// The subtotal for all items. Must equal the sum of (items[].unit_amount * items[].quantity) for all items. - #[serde(skip_serializing_if = "Option::is_none")] pub item_total: Option, /// The discount can be at the item or invoice level, or both. Can be applied as a percent or amount. If you provide both amount and percent, amount takes precedent. - #[serde(skip_serializing_if = "Option::is_none")] pub discount: Option, /// The aggregated amount of the item and shipping taxes. - #[serde(skip_serializing_if = "Option::is_none")] pub tax_total: Option, /// The shipping fee for all items. Includes tax on shipping. - #[serde(skip_serializing_if = "Option::is_none")] pub shipping: Option, /// The custom amount to apply to an invoice. If you include a label, you must include the custom amount. - #[serde(skip_serializing_if = "Option::is_none")] pub custom: Option, } /// Represents an amount of money. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Amount { /// The [three-character ISO-4217 currency code](https://developer.paypal.com/docs/integration/direct/rest/currency-codes/) that identifies the currency. @@ -426,7 +376,6 @@ pub struct Amount { /// For the required number of decimal places for a currency code, see [Currency Codes](https://developer.paypal.com/docs/api/reference/currency-codes/). pub value: String, /// The breakdown of the amount. Breakdown provides details such as total item amount, total tax amount, custom amount, shipping and discounts, if any. - #[serde(skip_serializing_if = "Option::is_none")] pub breakdown: Option, } @@ -480,72 +429,62 @@ impl Default for PaymentMethod { } /// Payment detail +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct PaymentDetail { /// The payment type in an invoicing flow which can be PayPal or an external cash or check payment. - #[serde(skip_serializing_if = "Option::is_none")] pub r#type: Option, /// The ID for a PayPal payment transaction. Required for the PAYPAL payment type. - #[serde(skip_serializing_if = "Option::is_none")] pub payment_id: Option, /// The date when the invoice was paid, in Internet date and time format. - #[serde(skip_serializing_if = "Option::is_none")] pub payment_date: Option>, /// The payment mode or method through which the invoicer can accept the payment. pub method: PaymentMethod, /// A note associated with an external cash or check payment. - #[serde(skip_serializing_if = "Option::is_none")] pub note: Option, /// The payment amount to record against the invoice. If you omit this parameter, the total invoice amount is marked as paid. This amount cannot exceed the amount due. - #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option, /// The recipient's shipping information. Includes the user's contact information, which includes name and address. - #[serde(skip_serializing_if = "Option::is_none")] pub shipping_info: Option, } /// Payments registered against the invoice +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Payments { /// The aggregated payment amounts against this invoice. /// Read only. - #[serde(skip_serializing_if = "Option::is_none")] pub paid_amount: Option, /// An array of payment details for the invoice. The payment details of the invoice like payment type, method, date, discount and transaction type. /// Read only. - #[serde(skip_serializing_if = "Option::is_none")] pub transactions: Option>, } /// Refund details +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct RefundDetail { /// The PayPal refund type. Indicates whether the refund was paid through PayPal or externally in the invoicing flow. - #[serde(skip_serializing_if = "Option::is_none")] pub r#type: Option, /// The ID for a PayPal payment transaction. Required for the PAYPAL payment type. - #[serde(skip_serializing_if = "Option::is_none")] pub refund_id: Option, /// The date when the invoice was refunded, in Internet date format. - #[serde(skip_serializing_if = "Option::is_none")] pub refund_date: Option>, /// The amount to record as refunded. If you omit the amount, the total invoice paid amount is recorded as refunded. - #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option, /// The payment mode or method through which the invoicer can accept the payments. pub method: PaymentMethod, } /// List of refunds +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct Refunds { /// The aggregated refund amounts. /// Read only. - #[serde(skip_serializing_if = "Option::is_none")] pub refund_amount: Option, /// An array of refund details for the invoice. Includes the refund type, date, amount, and method. /// Read only. - #[serde(skip_serializing_if = "Option::is_none")] pub transactions: Option>, } @@ -580,43 +519,37 @@ pub enum Status { } /// An invoice payload +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct InvoicePayload { /// The details of the invoice. Includes the invoice number, date, payment terms, and audit metadata. pub detail: InvoiceDetail, /// The invoicer information. Includes the business name, email, address, phone, fax, tax ID, additional notes, and logo URL. - #[serde(skip_serializing_if = "Option::is_none")] pub invoicer: Option, /// The billing and shipping information. Includes name, email, address, phone and language. - #[serde(skip_serializing_if = "Option::is_none")] pub primary_recipient: Option>, /// An array of one or more CC: emails to which notifications are sent. /// If you omit this parameter, a notification is sent to all CC: email addresses that are part of the invoice. - #[serde(skip_serializing_if = "Option::is_none")] pub additional_recipients: Option>, /// An array of invoice line item information. pub items: Vec, /// The invoice configuration details. Includes partial payment, tip, and tax calculated after discount. - #[serde(skip_serializing_if = "Option::is_none")] pub configuration: Option, /// The invoice amount summary of item total, discount, tax total and shipping.. - #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option, /// List of payments registered against the invoice. - #[serde(skip_serializing_if = "Option::is_none")] pub payments: Option, /// List of refunds against this invoice. The invoicing refund details includes refund type, date, amount, and method. - #[serde(skip_serializing_if = "Option::is_none")] pub refunds: Option, } /// Definition: https://developer.paypal.com/docs/api/invoicing/v2/#invoices_get +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct Invoice { /// The ID of the invoice. pub id: String, /// The parent ID to an invoice that defines the group invoice to which the invoice is related. - #[serde(skip_serializing_if = "Option::is_none")] pub parent_id: Option, /// The status of the invoice. pub status: Status, @@ -632,24 +565,18 @@ pub struct Invoice { /// An array of invoice line item information. pub items: Option>, /// The invoice configuration details. Includes partial payment, tip, and tax calculated after discount. - #[serde(skip_serializing_if = "Option::is_none")] pub configuration: Option, /// The invoice amount summary of item total, discount, tax total and shipping.. pub amount: Amount, /// The due amount, which is the balance amount outstanding after payments. - #[serde(skip_serializing_if = "Option::is_none")] pub due_amount: Option, /// The amount paid by the payer as gratuity to the invoicer. - #[serde(skip_serializing_if = "Option::is_none")] pub gratuity: Option, /// List of payments registered against the invoice.. - #[serde(skip_serializing_if = "Option::is_none")] pub payments: Option, /// List of refunds against this invoice. The invoicing refund details includes refund type, date, amount, and method. - #[serde(skip_serializing_if = "Option::is_none")] pub refunds: Option, /// An array of request-related HATEOAS links. - #[serde(skip_serializing_if = "Option::is_none")] pub links: Option>, } @@ -667,23 +594,19 @@ pub struct InvoiceList { } /// Cancel invoice reason +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct CancelReason { /// The subject of the email that is sent as a notification to the recipient. - #[serde(skip_serializing_if = "Option::is_none")] pub subject: Option, /// A note to the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub note: Option, /// Indicates whether to send a copy of the email to the merchant. - #[serde(skip_serializing_if = "Option::is_none")] pub send_to_invoicer: Option, /// Indicates whether to send a copy of the email to the recipient. - #[serde(skip_serializing_if = "Option::is_none")] pub send_to_recipient: Option, /// An array of one or more CC: emails to which notifications are sent. /// If you omit this parameter, a notification is sent to all CC: email addresses that are part of the invoice. - #[serde(skip_serializing_if = "Option::is_none")] pub additional_recipients: Option>, } @@ -706,17 +629,17 @@ pub struct QRCodeParams { } /// Used to record a payment. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Default)] pub struct RecordPaymentPayload { - #[serde(skip_serializing_if = "Option::is_none")] payment_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] + payment_date: Option>, method: PaymentMethod, - #[serde(skip_serializing_if = "Option::is_none")] + note: Option, amount: Amount, - #[serde(skip_serializing_if = "Option::is_none")] + shipping_info: Option, } @@ -831,11 +754,7 @@ impl super::Client { } /// Delete a invoice - pub async fn delete_invoice( - &mut self, - invoice_id: &str, - header_params: HeaderParams, - ) -> Result<(), ResponseError> { + pub async fn delete_invoice(&mut self, invoice_id: &str, header_params: HeaderParams) -> Result<(), ResponseError> { let build = self .setup_headers( self.client diff --git a/src/lib.rs b/src/lib.rs index 484148e..5b9a07c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ //! //! ## Example -//! +//! //! ```rust //! use paypal_rs::{ //! Client, @@ -21,22 +21,22 @@ //! orders::{OrderPayload, Intent, PurchaseUnit, Amount}, //! common::Currency, //! }; -//! +//! //! #[tokio::main] //! async fn main() { //! dotenv::dotenv().ok(); //! let clientid = std::env::var("PAYPAL_CLIENTID").unwrap(); //! let secret = std::env::var("PAYPAL_SECRET").unwrap(); -//! +//! //! let mut client = Client::new(clientid, secret, true); -//! +//! //! client.get_access_token().await.unwrap(); -//! +//! //! let order_payload = OrderPayload::new( //! Intent::Authorize, //! vec![PurchaseUnit::new(Amount::new(Currency::EUR, "10.0"))], //! ); -//! +//! //! let order = client //! .create_order( //! order_payload, @@ -49,14 +49,14 @@ //! .unwrap(); //! } //! ``` -//! +//! //! ## Testing //! You need the enviroment variables PAYPAL_CLIENTID and PAYPAL_SECRET to be set. -//! +//! //! `cargo test` -//! +//! //! ## Roadmap -//! +//! //! - [x] Orders API - 0.1.0 //! - - [x] Create order //! - - [x] Update order @@ -81,15 +81,16 @@ #![deny(missing_docs)] pub mod common; +pub mod countries; pub mod errors; pub mod invoice; pub mod orders; -pub mod countries; use errors::{PaypalError, ResponseError}; use reqwest::header; use reqwest::header::HeaderMap; use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; use std::time::{Duration, Instant}; /// The paypal api endpoint used on a live application. @@ -149,41 +150,32 @@ pub struct Client { /// use paypal_rs::Query; /// let query = Query { count: Some(40), ..Default::default() }; /// ``` +#[skip_serializing_none] #[derive(Debug, Default, Serialize)] pub struct Query { /// The number of items to list in the response. - #[serde(skip_serializing_if = "Option::is_none")] pub count: Option, /// The end date and time for the range to show in the response. - #[serde(skip_serializing_if = "Option::is_none")] pub end_time: Option>, /// The page number indicating which set of items will be returned in the response. /// So, the combination of page=1 and page_size=20 returns the first 20 items. /// The combination of page=2 and page_size=20 returns items 21 through 40. - #[serde(skip_serializing_if = "Option::is_none")] pub page: Option, /// The number of items to return in the response. - #[serde(skip_serializing_if = "Option::is_none")] pub page_size: Option, /// Indicates whether to show the total count in the response. - #[serde(skip_serializing_if = "Option::is_none")] pub total_count_required: Option, /// Sorts the payments in the response by a specified value, such as the create time or update time. - #[serde(skip_serializing_if = "Option::is_none")] pub sort_by: Option, /// Sorts the items in the response in ascending or descending order. - #[serde(skip_serializing_if = "Option::is_none")] pub sort_order: Option, /// The ID of the starting resource in the response. /// When results are paged, you can use the next_id value as the start_id to continue with the next set of results. - #[serde(skip_serializing_if = "Option::is_none")] pub start_id: Option, /// The start index of the payments to list. Typically, you use the start_index to jump to a specific position in the resource history based on its cart. /// For example, to start at the second item in a list of results, specify start_index=2. - #[serde(skip_serializing_if = "Option::is_none")] pub start_index: Option, /// The start date and time for the range to show in the response. - #[serde(skip_serializing_if = "Option::is_none")] pub start_time: Option>, // TODO: Use https://github.com/samscott89/serde_qs } diff --git a/src/orders.rs b/src/orders.rs index 83377b6..096bd9b 100644 --- a/src/orders.rs +++ b/src/orders.rs @@ -4,10 +4,11 @@ //! //! Reference: https://developer.paypal.com/docs/api/orders/v2/ -use crate::HeaderParams; use crate::common::*; -use crate::errors::{ResponseError, PaypalError}; +use crate::errors::{PaypalError, ResponseError}; +use crate::HeaderParams; use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; /// The intent to either capture payment immediately or authorize a payment for an order after order creation. #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] @@ -52,10 +53,10 @@ pub struct PhoneNumber { /// The phone number of the customer. Available only when you enable the /// Contact Telephone Number option in the Profile & Settings for the merchant's PayPal account. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct Phone { /// The phone type. - #[serde(skip_serializing_if = "Option::is_none")] pub phone_type: Option, /// The phone number pub phone_number: PhoneNumber, @@ -85,60 +86,49 @@ pub struct TaxInfo { /// The customer who approves and pays for the order. The customer is also known as the payer. /// /// https://developer.paypal.com/docs/api/orders/v2/#definition-payer +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Payer { /// The name of the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// The email address of the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub email_address: Option, /// The PayPal-assigned ID for the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub payer_id: Option, /// The phone number of the customer. Available only when you enable the Contact /// Telephone Number option in the Profile & Settings for the merchant's PayPal account. - #[serde(skip_serializing_if = "Option::is_none")] pub phone: Option, /// The birth date of the payer in YYYY-MM-DD format. - #[serde(skip_serializing_if = "Option::is_none")] pub birth_date: Option, /// The tax information of the payer. Required only for Brazilian payer's. - #[serde(skip_serializing_if = "Option::is_none")] pub tax_info: Option, /// The address of the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub address: Option
, } /// Breakdown provides details such as total item amount, total tax amount, shipping, handling, insurance, and discounts, if any. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Breakdown { /// The subtotal for all items. Required if the request includes purchase_units[].items[].unit_amount. /// Must equal the sum of (items[].unit_amount * items[].quantity) for all items. - #[serde(skip_serializing_if = "Option::is_none")] pub item_total: Option, /// The shipping fee for all items within a given purchase_unit. - #[serde(skip_serializing_if = "Option::is_none")] pub shipping: Option, /// The handling fee for all items within a given purchase_unit. - #[serde(skip_serializing_if = "Option::is_none")] pub handling: Option, /// The total tax for all items. Required if the request includes purchase_units.items.tax. Must equal the sum of (items[].tax * items[].quantity) for all items. - #[serde(skip_serializing_if = "Option::is_none")] pub tax_total: Option, /// The insurance fee for all items within a given purchase_unit. - #[serde(skip_serializing_if = "Option::is_none")] pub insurance: Option, /// The shipping discount for all items within a given purchase_unit. - #[serde(skip_serializing_if = "Option::is_none")] pub shipping_discount: Option, /// The discount for all items within a given purchase_unit. - #[serde(skip_serializing_if = "Option::is_none")] pub discount: Option, } /// Represents an amount of money. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Amount { /// The [three-character ISO-4217 currency code](https://developer.paypal.com/docs/integration/direct/rest/currency-codes/) that identifies the currency. @@ -150,7 +140,6 @@ pub struct Amount { /// For the required number of decimal places for a currency code, see [Currency Codes](https://developer.paypal.com/docs/api/reference/currency-codes/). pub value: String, /// The breakdown of the amount. - #[serde(skip_serializing_if = "Option::is_none")] pub breakdown: Option, } @@ -166,22 +155,22 @@ impl Amount { } /// The merchant who receives payment for this transaction. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct Payee { /// The email address of merchant. - #[serde(skip_serializing_if = "Option::is_none")] pub email_address: Option, /// The encrypted PayPal account ID of the merchant. - #[serde(skip_serializing_if = "Option::is_none")] pub merchant_id: Option, } /// Fees, commissions, tips, or donations +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct PlatformFee { /// The fee for this transaction. pub amount: Money, - #[serde(skip_serializing_if = "Option::is_none")] + /// The merchant who receives payment for this transaction. pub payee: Option, } @@ -204,13 +193,12 @@ impl Default for DisbursementMode { } /// Any additional payment instructions for PayPal Commerce Platform customers. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct PaymentInstruction { /// An array of various fees, commissions, tips, or donations. - #[serde(skip_serializing_if = "Option::is_none")] pub platform_fees: Option>, /// The funds that are held on behalf of the merchant. - #[serde(skip_serializing_if = "Option::is_none")] pub disbursement_mode: Option, } @@ -233,17 +221,17 @@ impl Default for ItemCategoryType { } /// The name and address of the person to whom to ship the items. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct ShippingDetail { /// The name of the person to whom to ship the items. Supports only the full_name property. - #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// The address of the person to whom to ship the items. - #[serde(skip_serializing_if = "Option::is_none")] pub address: Option
, } /// Represents an item. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct Item { /// The item name or title. @@ -252,18 +240,14 @@ pub struct Item { /// If you specify unit_amount, purchase_units[].amount.breakdown.item_total is required. Must equal unit_amount * quantity for all items. pub unit_amount: Money, /// The item tax for each unit. If tax is specified, purchase_units[].amount.breakdown.tax_total is required. Must equal tax * quantity for all items. - #[serde(skip_serializing_if = "Option::is_none")] pub tax: Option, /// The item quantity. Must be a whole number. pub quantity: String, /// The detailed item description. - #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, /// The stock keeping unit (SKU) for the item. - #[serde(skip_serializing_if = "Option::is_none")] pub sku: Option, /// The item category type - #[serde(skip_serializing_if = "Option::is_none")] pub category: Option, } @@ -404,11 +388,11 @@ pub struct PaymentCollection { } /// Represents either a full or partial order that the payer intends to purchase from the payee. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct PurchaseUnit { /// The API caller-provided external ID for the purchase unit. Required for multiple purchase units when you must update the order through PATCH. /// If you omit this value and the order contains only one purchase unit, PayPal sets this value to default. - #[serde(skip_serializing_if = "Option::is_none")] pub reference_id: Option, /// The total order amount with an optional breakdown that provides details, such as the total item amount, /// total tax amount, shipping, handling, insurance, and discounts, if any. @@ -419,43 +403,33 @@ pub struct PurchaseUnit { /// see the PayPal REST APIs [Currency Codes](https://developer.paypal.com/docs/integration/direct/rest/currency-codes/). pub amount: Amount, /// The merchant who receives payment for this transaction. - #[serde(skip_serializing_if = "Option::is_none")] pub payee: Option, /// Any additional payment instructions for PayPal Commerce Platform customers. /// Enables features for the PayPal Commerce Platform, such as delayed disbursement and collection of a platform fee. /// Applies during order creation for captured payments or during capture of authorized payments. - #[serde(skip_serializing_if = "Option::is_none")] pub payment_instruction: Option, /// The purchase description. - #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, /// The API caller-provided external ID. Used to reconcile client transactions with PayPal transactions. /// Appears in transaction and settlement reports but is not visible to the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub custom_id: Option, /// The API caller-provided external invoice number for this order. /// Appears in both the payer's transaction history and the emails that the payer receives. - #[serde(skip_serializing_if = "Option::is_none")] pub invoice_id: Option, /// The PayPal-generated ID for the purchase unit. /// This ID appears in both the payer's transaction history and the emails that the payer receives. /// In addition, this ID is available in transaction and settlement reports that merchants and API callers can use to reconcile transactions. /// This ID is only available when an order is saved by calling v2/checkout/orders/id/save. - #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, /// The soft descriptor is the dynamic text used to construct the statement descriptor that appears on a payer's card statement. /// /// More info here: https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit_request - #[serde(skip_serializing_if = "Option::is_none")] pub soft_descriptor: Option, /// An array of items that the customer purchases from the merchant. - #[serde(skip_serializing_if = "Option::is_none")] pub items: Option>, /// The name and address of the person to whom to ship the items. - #[serde(skip_serializing_if = "Option::is_none")] pub shipping: Option, /// The comprehensive history of payments for the purchase unit. - #[serde(skip_serializing_if = "Option::is_none")] pub payments: Option, } @@ -547,60 +521,51 @@ impl Default for PayeePreferred { } /// A payment method. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct PaymentMethod { /// The customer-selected payment method on the merchant site. - #[serde(skip_serializing_if = "Option::is_none")] pub payer_selected: Option, /// The merchant-preferred payment sources. - #[serde(skip_serializing_if = "Option::is_none")] pub payee_preferred: Option, } /// Customize the payer experience during the approval process for the payment with PayPal. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct ApplicationContext { /// The label that overrides the business name in the PayPal account on the PayPal site. - #[serde(skip_serializing_if = "Option::is_none")] pub brand_name: Option, /// The BCP 47-formatted locale of pages that the PayPal payment experience shows. PayPal supports a five-character code. /// /// For example, da-DK, he-IL, id-ID, ja-JP, no-NO, pt-BR, ru-RU, sv-SE, th-TH, zh-CN, zh-HK, or zh-TW. - #[serde(skip_serializing_if = "Option::is_none")] pub locale: Option, /// The type of landing page to show on the PayPal site for customer checkout - #[serde(skip_serializing_if = "Option::is_none")] pub landing_page: Option, /// The shipping preference - #[serde(skip_serializing_if = "Option::is_none")] pub shipping_preference: Option, /// Configures a Continue or Pay Now checkout flow. - #[serde(skip_serializing_if = "Option::is_none")] pub user_action: Option, /// The customer and merchant payment preferences. - #[serde(skip_serializing_if = "Option::is_none")] pub payment_method: Option, /// The URL where the customer is redirected after the customer approves the payment. - #[serde(skip_serializing_if = "Option::is_none")] pub return_url: Option, /// The URL where the customer is redirected after the customer cancels the payment. - #[serde(skip_serializing_if = "Option::is_none")] pub cancel_url: Option, } /// A order payload to be used when creating an order. +#[skip_serializing_none] #[derive(Debug, Default, Serialize, Deserialize)] pub struct OrderPayload { /// The intent to either capture payment immediately or authorize a payment for an order after order creation. pub intent: Intent, /// The customer who approves and pays for the order. The customer is also known as the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub payer: Option, /// An array of purchase units. Each purchase unit establishes a contract between a payer and the payee. /// Each purchase unit represents either a full or partial order that the payer intends to purchase from the payee. pub purchase_units: Vec, /// Customize the payer experience during the approval process for the payment with PayPal. - #[serde(skip_serializing_if = "Option::is_none")] pub application_context: Option, } @@ -709,28 +674,23 @@ pub enum OrderStatus { } /// An order represents a payment between two or more parties. +#[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct Order { /// The date and time when the transaction occurred. - #[serde(skip_serializing_if = "Option::is_none")] pub create_time: Option>, /// The date and time when the transaction was last updated. - #[serde(skip_serializing_if = "Option::is_none")] pub update_time: Option>, /// The ID of the order. pub id: String, /// The payment source used to fund the payment. - #[serde(skip_serializing_if = "Option::is_none")] pub payment_source: Option, /// The intent to either capture payment immediately or authorize a payment for an order after order creation. - #[serde(skip_serializing_if = "Option::is_none")] pub intent: Option, /// The customer who approves and pays for the order. The customer is also known as the payer. - #[serde(skip_serializing_if = "Option::is_none")] pub payer: Option, /// An array of purchase units. Each purchase unit establishes a contract between a customer and merchant. /// Each purchase unit represents either a full or partial order that the customer intends to purchase from the merchant. - #[serde(skip_serializing_if = "Option::is_none")] pub purchase_units: Option>, /// The order status. pub status: OrderStatus, @@ -882,10 +842,7 @@ impl super::Client { } /// Shows details for an order, by ID. - pub async fn show_order_details( - &mut self, - order_id: &str, - ) -> Result { + pub async fn show_order_details(&mut self, order_id: &str) -> Result { self.build_endpoint_order(order_id, "", false, HeaderParams::default()) .await }