Reorder and invoice progress

This commit is contained in:
Edgar 2020-08-12 10:34:59 +02:00
parent b23a7663a3
commit 760aeada94
No known key found for this signature in database
GPG key ID: 8731E6C0166EAA85
7 changed files with 695 additions and 119 deletions

View file

@ -26,7 +26,7 @@ async fn main() {
let clientid = std::env::var("PAYPAL_CLIENTID").unwrap();
let secret = std::env::var("PAYPAL_SECRET").unwrap();
let mut client = Client::new(clientid.as_str(), secret.as_str(), true);
let mut client = Client::new(clientid, secret, true);
client.get_access_token().await.unwrap();

82
src/common.rs Normal file
View file

@ -0,0 +1,82 @@
/// Common paypal object definitions used amon 2 or more APIs
use serde::{Serialize, Deserialize};
/// The phone type.
///
/// https://developer.paypal.com/docs/api/orders/v2/#definition-phone_with_type
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(missing_docs)]
pub enum PhoneType {
Fax,
Home,
Mobile,
Other,
Pager,
}
/// The non-portable additional address details
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AddressDetails {
/// The street number.
#[serde(skip_serializing_if = "Option::is_none")]
pub street_number: Option<String>,
/// The street name. Just Drury in Drury Lane.
#[serde(skip_serializing_if = "Option::is_none")]
pub street_name: Option<String>,
/// The street type. For example, avenue, boulevard, road, or expressway.
#[serde(skip_serializing_if = "Option::is_none")]
pub street_type: Option<String>,
/// The delivery service. Post office box, bag number, or post office name.
#[serde(skip_serializing_if = "Option::is_none")]
pub delivery_service: Option<String>,
/// 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<String>,
/// 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<String>,
}
/// The address of the payer.
#[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<String>,
/// The second line of the address. For example, suite or apartment number.
#[serde(skip_serializing_if = "Option::is_none")]
pub address_line_2: Option<String>,
/// A city, town, or village. Smaller than admin_area_level_1.
#[serde(skip_serializing_if = "Option::is_none")]
pub admin_area_2: Option<String>,
/// 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<String>,
/// 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<String>,
/// 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<AddressDetails>,
}
/// Represents money
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Money {
/// The [three-character ISO-4217 currency code](https://developer.paypal.com/docs/integration/direct/rest/currency-codes/) that identifies the currency.
pub currency_code: String,
/// The value, which might be:
/// - An integer for currencies like JPY that are not typically fractional.
/// - A decimal fraction for currencies like TND that are subdivided into thousandths.
///
/// 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,
}

View file

@ -3,17 +3,7 @@ use std::collections::HashMap;
use std::fmt;
use std::error::Error;
use serde::{Deserialize, Serialize};
/// Represents a error HATEOAS link
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct ErrorLink {
/// The complete target URL.
pub href: String,
/// 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.
pub method: String,
}
use crate::objects::LinkDescription;
/// A paypal api response error.
#[derive(Debug, Serialize, Deserialize)]
@ -31,7 +21,7 @@ pub struct ApiResponseError {
/// Only available on Identity errors
pub error_description: Option<String>,
/// Links with more information about the error.
pub links: Vec<ErrorLink>,
pub links: Vec<LinkDescription>,
}
impl fmt::Display for ApiResponseError {

558
src/invoice.rs Normal file
View file

@ -0,0 +1,558 @@
//! Use the Invoicing API to create, send, and manage invoices.
//! You can also use the API or webhooks to track invoice payments. When you send an invoice to a customer,
//! the invoice moves from draft to payable state. PayPal then emails the customer a link to the invoice on the PayPal website.
//! Customers with a PayPal account can log in and pay the invoice with PayPal. Alternatively,
//! customers can pay as a guest with a debit card or credit card. For more information, see the Invoicing Overview and the Invoicing Integration Guide.
//!
//! Reference: https://developer.paypal.com/docs/api/invoicing/v2/
use crate::common::*;
use crate::errors;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Paypal File reference
#[derive(Debug, Serialize, Deserialize)]
pub struct FileReference {
/// The ID of the referenced file.
pub id: String,
/// The reference URL for the file.
pub reference_url: String,
/// Content type
pub content_type: String,
/// The date and time when the file was created
pub create_time: chrono::DateTime<chrono::Utc>,
/// The size of the file, in bytes.
pub size: String,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
/// The payment term type.
pub enum PaymentTermType {
/// The payment for the invoice is due upon receipt of the invoice.
DueOnReceipt,
/// The payment for the invoice is due on the date specified in the invoice.
DueOnDateSpecified,
/// The payment for the invoice is due in 10 days.
Net10,
/// The payment for the invoice is due in 15 days.
Net15,
/// The payment for the invoice is due in 30 days.
Net30,
/// The payment for the invoice is due in 45 days.
Net45,
/// The payment for the invoice is due in 60 days.
Net60,
/// The payment for the invoice is due in 90 days.
Net90,
/// The invoice has no payment due date.
NoDueDate,
}
/// The payment due date for the invoice.
#[derive(Debug, Serialize, Deserialize)]
pub struct PaymentTerm {
/// The payment term. Payment can be due upon receipt, a specified date, or in a set number of days
pub term_type: PaymentTermType,
/// The date when the invoice payment is due,
pub due_date: Option<chrono::DateTime<chrono::Utc>>,
}
/// Flow type
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
/// The flow variation
pub enum FlowType {
/// The invoice sent to multiple recipients.
MultipleRecipientsGroup,
/// The invoice sent as a batch.
Batch,
/// The regular invoice sent to single recipient.
RegularSingle,
}
/// Metadata about a resource
#[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<chrono::DateTime<chrono::Utc>>,
/// The email address of the account that created the resource.
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by: Option<String>,
/// The date and time when the resource was last edited
#[serde(skip_serializing_if = "Option::is_none")]
pub last_update_time: Option<chrono::DateTime<chrono::Utc>>,
/// The email address of the account that last edited the resource.
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated_by: Option<chrono::DateTime<chrono::Utc>>,
/// The date and time when the resource was canceled
#[serde(skip_serializing_if = "Option::is_none")]
pub cancel_time: Option<chrono::DateTime<chrono::Utc>>,
/// The actor who canceled the resource.
#[serde(skip_serializing_if = "Option::is_none")]
pub cancelled_by: Option<chrono::DateTime<chrono::Utc>>,
/// The date and time when the resource was first sent
#[serde(skip_serializing_if = "Option::is_none")]
pub first_sent_time: Option<chrono::DateTime<chrono::Utc>>,
/// The date and time when the resource was last sent
#[serde(skip_serializing_if = "Option::is_none")]
pub last_sent_time: Option<chrono::DateTime<chrono::Utc>>,
/// The email address of the account that last sent the resource.
#[serde(skip_serializing_if = "Option::is_none")]
pub last_sent_by: Option<String>,
/// The flow variation that created this invoice
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_flow: Option<FlowType>,
/// The URL for the invoice payer view hosted on paypal.com.
#[serde(skip_serializing_if = "Option::is_none")]
pub recipient_view_url: Option<String>,
/// The URL for the invoice merchant view hosted on paypal.com
#[serde(skip_serializing_if = "Option::is_none")]
pub invoicer_view_url: Option<String>,
}
/// The details of the invoice. Includes the invoice number, date, payment terms, and audit metadata.
#[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<String>,
/// 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<String>,
/// 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<String>,
/// A private bookkeeping memo for the user.
pub memo: Option<String>,
/// An array of PayPal IDs for the files that are attached to an invoice.
#[serde(skip_serializing_if = "Option::is_none")]
pub attachments: Option<Vec<FileReference>>,
/// The invoice number. Default is the number that is auto-incremented number from the last number.
pub invoice_number: String,
/// The invoice date as specificed by the sender
pub invoice_date: chrono::DateTime<chrono::Utc>,
/// The payment due date for the invoice.
pub payment_term: PaymentTerm,
/// The audit metadata
pub metadata: Metadata,
}
/// A name to be used as recipient, etc.
#[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<String>,
/// When the party is a person, the party's given, or first, name.
#[serde(skip_serializing_if = "Option::is_none")]
pub given_name: Option<String>,
/// 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<String>,
/// 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<String>,
/// The suffix for the party's name.
#[serde(skip_serializing_if = "Option::is_none")]
pub suffix: Option<String>,
/// 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<String>,
/// When the party is a person, the party's full name.
#[serde(skip_serializing_if = "Option::is_none")]
pub full_name: Option<String>,
}
/// Phone information
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct PhoneDetail {
/// The country calling code (CC), in its canonical international E.164 numbering plan format.
pub country_code: String,
/// 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<String>,
/// The phone type.
#[serde(skip_serializing_if = "Option::is_none")]
pub phone_type: Option<PhoneType>,
}
/// The invoicer information.
#[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<Name>,
/// 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<String>,
/// 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<Vec<PhoneDetail>>,
/// The invoicer's website.
#[serde(skip_serializing_if = "Option::is_none")]
pub website: Option<String>,
/// The invoicer's tax ID.
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_id: Option<String>,
/// Any additional information. Includes business hours.
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_notes: Option<String>,
/// 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<String>,
}
/// Billing information
#[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<Name>,
/// The address of the recipient.
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<Address>,
/// 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<String>,
/// The invoice recipient's phone numbers. Extension number is not supported.
#[serde(skip_serializing_if = "Option::is_none")]
pub phones: Option<Vec<PhoneDetail>>,
/// Any additional information about the recipient. Maximum length: 40.
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_info: Option<String>,
/// 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<String>,
}
/// Contact information
#[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<Name>,
/// The address of the recipient.
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<Address>,
}
/// Recipient information
#[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<BillingInfo>,
/// 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<ContactInformation>,
}
/// Tax information
#[derive(Debug, Serialize, Deserialize)]
pub struct Tax {
/// The name of the tax applied on the invoice items.
pub name: String,
/// 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<Money>,
}
/// Discount information
#[derive(Debug, Serialize, Deserialize)]
pub struct Discount {
/// The discount as a percentage value. Value is from 0 to 100. Supports up to five decimal places.
pub percent: Option<String>,
/// The invoice level discount amount. Value is from 0 to 1000000. Supports up to two decimal places.
pub amount: Option<String>,
}
/// The unit of measure for the invoiced item.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum UnitOffMeasure {
/// The unit of measure is quantity. This invoice template is typically used for physical goods.
Quantity,
/// The unit of measure is hours. This invoice template is typically used for services.
Hours,
/// The unit of measure is amount. This invoice template is typically used when only amount is required.
Amount,
}
/// Item information
#[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<String>,
/// 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<String>,
/// 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<Tax>,
/// 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<chrono::DateTime<chrono::Utc>>,
/// 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<Discount>,
/// 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<UnitOffMeasure>,
}
/// The partial payment details.
#[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<bool>,
/// 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<Money>,
}
/// The invoice configuration details. Includes partial payment, tip, and tax calculated after discount.
#[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<bool>,
/// Indicates whether the unit price includes tax.
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_inclusive: Option<bool>,
/// 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<bool>,
/// 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<PartialPayment>,
/// 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<String>,
}
/// The discount
#[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<Discount>,
/// 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<Money>,
}
/// The shipping fee
#[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<Money>,
/// The tax associated with the shipping.
#[serde(skip_serializing_if = "Option::is_none")]
pub tax: Option<Tax>,
}
#[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<Money>,
}
/// The breakdown of the amount. Breakdown provides details such as total item amount, total tax amount, custom amount, shipping and discounts, if any.
#[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<Money>,
/// 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<AggregatedDiscount>,
/// The aggregated amount of the item and shipping taxes.
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_total: Option<Money>,
/// The shipping fee for all items. Includes tax on shipping.
#[serde(skip_serializing_if = "Option::is_none")]
pub shipping: Option<ShippingCost>,
/// 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<CustomAmount>,
}
/// Represents an amount of money.
#[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.
pub currency_code: String,
/// The value, which might be:
/// - An integer for currencies like JPY that are not typically fractional.
/// - A decimal fraction for currencies like TND that are subdivided into thousandths.
///
/// 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<Breakdown>,
}
impl Amount {
/// Creates a new amount with the required values.
pub fn new<S: Into<String>>(currency_code: S, value: S) -> Self {
Amount {
currency_code: currency_code.into(),
value: value.into(),
breakdown: None,
}
}
}
/// The payment type in an invoicing flow
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PaymentType {
/// The payment type is PayPal.
Paypal,
/// The payment type is an external cash or a check payment.
External,
}
/// The payment mode or method through which the invoicer can accept the payment.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PaymentMethod {
/// Payments can be received through bank transfers.
BankTransfer,
/// Payments can be received as cash.
Cash,
/// Payments can be received as check.
Check,
/// Payments can be received through credit card payments.
CreditCard,
/// Payments can be received through debit card payments.
DebitCard,
/// Payments can be received through paypal payments.
Paypal,
/// Payments can be received through wire transfer.
WireTransfer,
/// Payments can be received through other modes.
Other,
}
#[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<PaymentType>,
/// The ID for a PayPal payment transaction. Required for the PAYPAL payment type.
#[serde(skip_serializing_if = "Option::is_none")]
pub payment_id: Option<String>,
/// The date when the invoice was paid, in Internet date and time format.
#[serde(skip_serializing_if = "Option::is_none")]
pub payment_date: Option<chrono::DateTime<chrono::Utc>>,
/// 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<String>,
/// 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<Money>,
/// 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<ContactInformation>,
}
#[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<Money>,
/// 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<Vec<PaymentDetail>>,
}
/// An invoice
#[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<InvoicerInfo>,
/// The billing and shipping information. Includes name, email, address, phone and language.
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_recipient: Option<Vec<RecipientInfo>>,
/// 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<Vec<String>>,
/// An array of invoice line item information.
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Vec<Item>>,
/// The invoice configuration details. Includes partial payment, tip, and tax calculated after discount.
#[serde(skip_serializing_if = "Option::is_none")]
pub configuration: Option<Configuration>,
#[serde(skip_serializing_if = "Option::is_none")]
pub amount: Option<Amount>,
}
impl super::Client {
/// Generates the next invoice number that is available to the merchant.
///
/// The next invoice number uses the prefix and suffix from the last invoice number and increments the number by one.
///
/// For example, the next invoice number after `INVOICE-1234` is `INVOICE-1235`.
pub async fn generate_invoice_number(
&self,
header_params: crate::HeaderParams,
) -> Result<String, Box<dyn std::error::Error>> {
let build = self.setup_headers(
self.client
.post(format!("{}/v2/invoicing/generate-next-invoice-number", self.endpoint()).as_str()),
header_params,
);
let res = build.send().await?;
if res.status().is_success() {
let x = res.json::<HashMap<String, String>>().await?;
Ok(x.get("invoice_number").expect("to have a invoice number").clone())
} else {
Err(Box::new(res.json::<errors::ApiResponseError>().await?))
}
}
}

View file

@ -1,13 +1,13 @@
//! # paypal-rs
//! # paypal-rs
//! ![Rust](https://github.com/edg-l/paypal-rs/workflows/Rust/badge.svg)
//! ![Docs](https://docs.rs/paypal-rs/badge.svg)
//!
//!
//! A rust library that wraps the [paypal api](https://developer.paypal.com/docs/api) asynchronously in a strongly typed manner.
//!
//!
//! Crate: https://crates.io/crates/paypal-rs
//!
//!
//! Documentation: https://docs.rs/paypal-rs
//!
//!
//! Currently in early development.
#![deny(missing_docs)]
@ -18,6 +18,8 @@ mod tests;
extern crate chrono;
pub mod errors;
pub mod invoice;
pub mod common;
pub mod orders;
use reqwest::header;
@ -175,8 +177,8 @@ impl Client {
/// let secret = std::env::var("PAYPAL_SECRET").unwrap();
///
/// let mut client = paypal_rs::Client::new(
/// clientid.as_str(),
/// secret.as_str(),
/// clientid,
/// secret,
/// true,
/// );
/// client.get_access_token().await.unwrap();
@ -211,7 +213,7 @@ impl Client {
if let Some(token) = &self.auth.access_token {
headers.append(
header::AUTHORIZATION,
format!("Bearer {}", token.access_token).as_str().parse().unwrap(),
format!("Bearer {}", token.access_token).parse().unwrap(),
);
}
@ -228,25 +230,19 @@ impl Client {
)
.unwrap();
let encoded_token = base64::encode(token);
headers.append("PayPal-Auth-Assertion", encoded_token.as_str().parse().unwrap());
headers.append("PayPal-Auth-Assertion", encoded_token.parse().unwrap());
}
if let Some(client_metadata_id) = header_params.client_metadata_id {
headers.append(
"PayPal-Client-Metadata-Id",
client_metadata_id.as_str().parse().unwrap(),
);
headers.append("PayPal-Client-Metadata-Id", client_metadata_id.parse().unwrap());
}
if let Some(partner_attribution_id) = header_params.partner_attribution_id {
headers.append(
"PayPal-Partner-Attribution-Id",
partner_attribution_id.as_str().parse().unwrap(),
);
headers.append("PayPal-Partner-Attribution-Id", partner_attribution_id.parse().unwrap());
}
if let Some(request_id) = header_params.request_id {
headers.append("PayPal-Request-Id", request_id.as_str().parse().unwrap());
headers.append("PayPal-Request-Id", request_id.parse().unwrap());
}
if let Some(prefer) = header_params.prefer {
@ -257,7 +253,7 @@ impl Client {
}
if let Some(content_type) = header_params.content_type {
headers.append(header::CONTENT_TYPE, content_type.as_str().parse().unwrap());
headers.append(header::CONTENT_TYPE, content_type.parse().unwrap());
}
builder.headers(headers)
@ -268,7 +264,7 @@ impl Client {
let res = self
.client
.post(format!("{}/v1/oauth2/token", self.endpoint()).as_str())
.basic_auth(self.auth.client_id.as_str(), Some(self.auth.secret.as_str()))
.basic_auth(&self.auth.client_id, Some(&self.auth.secret))
.header("Content-Type", "x-www-form-urlencoded")
.header("Accept", "application/json")
.body("grant_type=client_credentials")

View file

@ -6,6 +6,7 @@
use crate::errors;
use serde::{Deserialize, Serialize};
use crate::common::*;
/// The intent to either capture payment immediately or authorize a payment for an order after order creation.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
@ -39,20 +40,6 @@ pub struct PayerName {
pub surname: String,
}
/// The phone type.
///
/// https://developer.paypal.com/docs/api/orders/v2/#definition-phone_with_type
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(missing_docs)]
pub enum PhoneType {
Fax,
Home,
Mobile,
Other,
Pager,
}
/// The phone number, in its canonical international E.164 numbering plan format.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct PhoneNumber {
@ -66,7 +53,7 @@ pub struct PhoneNumber {
/// Contact Telephone Number option in the Profile & Settings for the merchant's PayPal account.
#[derive(Debug, Serialize, Deserialize)]
pub struct Phone {
/// The phone type.
/// The phone type.
#[serde(skip_serializing_if = "Option::is_none")]
pub phone_type: Option<PhoneType>,
/// The phone number
@ -94,30 +81,6 @@ pub struct TaxInfo {
pub tax_id_type: TaxIdType,
}
/// The address of the payer.
#[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<String>,
/// The second line of the address. For example, suite or apartment number.
#[serde(skip_serializing_if = "Option::is_none")]
pub address_line_2: Option<String>,
/// A city, town, or village. Smaller than admin_area_level_1.
#[serde(skip_serializing_if = "Option::is_none")]
pub admin_area_2: Option<String>,
/// 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<String>,
/// 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<String>,
/// 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 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
@ -147,19 +110,6 @@ pub struct Payer {
pub address: Option<Address>,
}
/// Represents money
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Money {
/// The [three-character ISO-4217 currency code](https://developer.paypal.com/docs/integration/direct/rest/currency-codes/) that identifies the currency.
pub currency_code: String,
/// The value, which might be:
/// - An integer for currencies like JPY that are not typically fractional.
/// - A decimal fraction for currencies like TND that are subdivided into thousandths.
///
/// 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,
}
/// Breakdown provides details such as total item amount, total tax amount, shipping, handling, insurance, and discounts, if any.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Breakdown {
@ -228,7 +178,7 @@ pub struct Payee {
/// Fees, commissions, tips, or donations
#[derive(Debug, Serialize, Deserialize)]
pub struct PlatformFee {
/// The fee for this transaction.
/// The fee for this transaction.
pub amount: Money,
#[serde(skip_serializing_if = "Option::is_none")]
/// The merchant who receives payment for this transaction.
@ -252,7 +202,7 @@ impl Default for DisbursementMode {
}
}
/// Any additional payment instructions for PayPal Commerce Platform customers.
/// Any additional payment instructions for PayPal Commerce Platform customers.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct PaymentInstruction {
/// An array of various fees, commissions, tips, or donations.
@ -284,10 +234,10 @@ impl Default for ItemCategoryType {
/// The name and address of the person to whom to ship the items.
#[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.
/// 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<String>,
/// The address of the person to whom to ship the items.
/// The address of the person to whom to ship the items.
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<Address>,
}
@ -452,7 +402,7 @@ pub struct PaymentCollection {
pub refunds: Vec<Refund>,
}
/// Represents either a full or partial order that the payer intends to purchase from the payee.
/// Represents either a full or partial order that the payer intends to purchase from the payee.
#[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.
@ -598,7 +548,7 @@ impl Default for PayeePreferred {
/// A payment method.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct PaymentMethod {
/// The customer-selected payment method on the merchant site.
/// The customer-selected payment method on the merchant site.
#[serde(skip_serializing_if = "Option::is_none")]
pub payer_selected: Option<String>,
/// The merchant-preferred payment sources.
@ -606,7 +556,7 @@ pub struct PaymentMethod {
pub payee_preferred: Option<PayeePreferred>,
}
/// Customize the payer experience during the approval process for the payment with PayPal.
/// Customize the payer experience during the approval process for the payment with PayPal.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ApplicationContext {
/// The label that overrides the business name in the PayPal account on the PayPal site.
@ -724,10 +674,10 @@ pub struct CardResponse {
pub card_type: CardType,
}
/// The customer's wallet used to fund the transaction.
/// The customer's wallet used to fund the transaction.
#[derive(Debug, Serialize, Deserialize)]
pub struct WalletResponse {
/// Apple Pay Wallet response information.
/// Apple Pay Wallet response information.
pub apple_pay: CardResponse,
}
@ -736,27 +686,10 @@ pub struct WalletResponse {
pub struct PaymentSourceResponse {
/// The payment card to use to fund a payment. Card can be a credit or debit card
pub card: CardResponse,
/// The customer's wallet used to fund the transaction.
/// The customer's wallet used to fund the transaction.
pub wallet: WalletResponse,
}
/// The status of an order.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OrderStatus {
/// The order was created with the specified context.
Created,
/// The order was saved and persisted. The order status continues to be in progress until a capture
/// is made with final_capture = true for all purchase units within the order.
Saved,
/// The customer approved the payment through the PayPal wallet or another form of guest or unbranded payment. For example, a card, bank account, or so on.
Approved,
/// All purchase units in the order are voided.
Voided,
/// The payment was authorized or the authorized payment was captured for the order.
Completed,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(missing_docs)]
@ -783,6 +716,23 @@ pub struct LinkDescription {
pub method: Option<LinkMethod>,
}
/// The status of an order.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OrderStatus {
/// The order was created with the specified context.
Created,
/// The order was saved and persisted. The order status continues to be in progress until a capture
/// is made with final_capture = true for all purchase units within the order.
Saved,
/// The customer approved the payment through the PayPal wallet or another form of guest or unbranded payment. For example, a card, bank account, or so on.
Approved,
/// All purchase units in the order are voided.
Voided,
/// The payment was authorized or the authorized payment was captured for the order.
Completed,
}
/// An order represents a payment between two or more parties.
#[derive(Debug, Serialize, Deserialize)]
pub struct Order {
@ -823,7 +773,7 @@ impl super::Client {
let builder = {
self.setup_headers(
self.client
.post(format!("{}/v2/checkout/orders", self.endpoint()).as_str()),
.post(&format!("{}/v2/checkout/orders", self.endpoint())),
header_params,
)
};
@ -849,8 +799,8 @@ impl super::Client {
let builder = self.setup_headers(
match post {
true => self.client.post(format.as_str()),
false => self.client.get(format.as_str()),
true => self.client.post(&format),
false => self.client.get(&format),
},
header_params,
);
@ -901,7 +851,7 @@ impl super::Client {
unit_json += ",";
}
units_json += unit_json.as_str();
units_json.push_str(&unit_json);
}
}
@ -934,7 +884,7 @@ impl super::Client {
let builder = {
self.setup_headers(
self.client
.patch(format!("{}/v2/checkout/orders/{}", self.endpoint(), id).as_str()),
.patch(&format!("{}/v2/checkout/orders/{}", self.endpoint(), id)),
crate::HeaderParams {
content_type: Some(String::from("application/json")),
..Default::default()

View file

@ -1,5 +1,5 @@
use crate::{
orders::{Amount, Intent, OrderPayload, OrderStatus, PurchaseUnit},
objects::*,
Client, HeaderParams, Prefer,
};
use std::env;
@ -10,7 +10,7 @@ async fn it_works() {
let clientid = env::var("PAYPAL_CLIENTID").unwrap();
let secret = env::var("PAYPAL_SECRET").unwrap();
let mut client = Client::new(clientid.as_str(), secret.as_str(), true);
let mut client = Client::new(clientid, secret, true);
assert_eq!(client.get_access_token().await.is_err(), false, "should not error");