diff --git a/CHANGELOG.md b/CHANGELOG.md index f0364cb9..de61f5b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 13.0.0 - 2021-08-01 +* [#133](https://github.com/vapor-community/stripe-kit/pull/133) + * ⚠️ Breaking changes ⚠️ Multiple API updates. + * Adds support for `Quotes` and `QuoteLineItems` + * Adds support for `VerificationSessions` and `VerificationReports` + ## 12.0.2 - 2021-04-29 * [#125](https://github.com/vapor-community/stripe-kit/pull/125) Added payment method models. diff --git a/README.md b/README.md index f1354bbb..8880690e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ To start using StripeKit, in your `Package.swift`, add the following ~~~~swift -.package(url: "https://github.com/vapor-community/stripe-kit.git", from: "12.0.0") +.package(url: "https://github.com/vapor-community/stripe-kit.git", from: "13.0.0") ~~~~ ## Using the API @@ -230,8 +230,6 @@ See the [Vapor helper library](https://github.com/vapor-community/stripe) to use * [x] SetupIntents * [x] SetupAttempts * [x] Payouts -* [x] Prices -* [x] Products * [x] Refunds * [x] Tokens --- @@ -241,25 +239,30 @@ See the [Vapor helper library](https://github.com/vapor-community/stripe) to use * [x] Cards * [x] Sources --- +### Products +* [x] Products +* [x] Prices +* [x] Coupons +* [x] Promotion Codes +* [x] Discounts +* [x] Tax Rates +--- ### Checkout * [x] Sessions --- ### Billing -* [x] Coupons * [x] Credit Notes * [x] Customer Balance Transactions * [x] Customer Portal * [x] Customer Tax IDs -* [x] Discounts * [x] Invoices * [x] Invoice Items * [x] Plans -* [x] Promotion Codes -* [x] Products +* [x] Quotes +* [x] Quote Line Items * [x] Subscriptions * [x] Subscription items * [x] Subscription Schedule -* [x] Tax Rates * [x] Usage Records --- ### Connect @@ -307,6 +310,10 @@ See the [Vapor helper library](https://github.com/vapor-community/stripe) to use * [x] Report Runs * [x] Report Types --- +### Identity +* [x] VerificationSessions +* [x] VerificationReports +--- ### Webhooks * [x] Webhook Endpoints * [x] Signature Verification diff --git a/Sources/StripeKit/Billing/Invoices/Invoice.swift b/Sources/StripeKit/Billing/Invoices/Invoice.swift index 9cc20356..51a86edc 100644 --- a/Sources/StripeKit/Billing/Invoices/Invoice.swift +++ b/Sources/StripeKit/Billing/Invoices/Invoice.swift @@ -107,6 +107,8 @@ public struct StripeInvoice: StripeModel { public var postPaymentCreditNotesAmount: Int? /// Total amount of all pre-payment credit notes issued for this invoice. public var prePaymentCreditNotesAmount: Int? + /// The quote this invoice was generated from. + @Expandable public var quote: String? /// This is the transaction number that appears on email receipts sent for this invoice. public var receiptNumber: String? /// Starting customer balance before the invoice is finalized. If the invoice has not been finalized yet, this will be the current customer balance. diff --git a/Sources/StripeKit/Billing/Invoices/InvoiceRoutes.swift b/Sources/StripeKit/Billing/Invoices/InvoiceRoutes.swift index a9d4f9c3..d840ca3b 100644 --- a/Sources/StripeKit/Billing/Invoices/InvoiceRoutes.swift +++ b/Sources/StripeKit/Billing/Invoices/InvoiceRoutes.swift @@ -71,8 +71,8 @@ public protocol InvoiceRoutes { /// - applicationFeeAmount: A fee in cents that will be applied to the invoice and transferred to the application owner’s Stripe account. The request must be made with an OAuth key or the Stripe-Account header in order to take an application fee. For more information, see the application fees documentation. /// - autoAdvance: Controls whether Stripe will perform automatic collection of the invoice. /// - collectionMethod: Either `charge_automatically`, or `send_invoice`. When charging automatically, Stripe will attempt to pay this invoice using the default source attached to the customer. When sending an invoice, Stripe will email this invoice to the customer with payment instructions. Defaults to `charge_automatically`. - /// - customFields: A list of up to 4 custom fields to be displayed on the invoice. If a value for custom_fields is specified, the list specified will replace the existing custom field list on this invoice. - /// - daysUntilDue: The number of days from which the invoice is created until it is due. Only valid for invoices where billing=send_invoice. This field can only be updated on draft invoices. + /// - customFields: A list of up to 4 custom fields to be displayed on the invoice. If a value for `custom_fields` is specified, the list specified will replace the existing custom field list on this invoice. + /// - daysUntilDue: The number of days from which the invoice is created until it is due. Only valid for invoices where `billing=send_invoice`. This field can only be updated on draft invoices. /// - defaultPaymentMethod: ID of the default payment method for the invoice. It must belong to the customer associated with the invoice. If not set, defaults to the subscription’s default payment method, if any, or to the default payment method in the customer’s invoice settings. /// - defaultSource: ID of the default payment source for the invoice. It must belong to the customer associated with the invoice and be in a chargeable state. If not set, defaults to the subscription’s default source, if any, or to the customer’s default source. /// - defaultTaxRates: The tax rates that will apply to any line item that does not have `tax_rates` set. Pass an empty string to remove previously-defined tax rates. diff --git a/Sources/StripeKit/Billing/Quote Line Items/QuoteLineItem.swift b/Sources/StripeKit/Billing/Quote Line Items/QuoteLineItem.swift new file mode 100644 index 00000000..9d10bcc8 --- /dev/null +++ b/Sources/StripeKit/Billing/Quote Line Items/QuoteLineItem.swift @@ -0,0 +1,54 @@ +// +// QuoteLineItem.swift +// StripeKit +// +// Created by Andrew Edwards on 7/25/21. +// + +import Foundation + +public struct StripeQuoteLineItem: StripeModel { + /// Unique identifier for the object. + public var id: String + /// String representing the object’s type. Objects of the same type share the same value. + public var object: String + /// Total before any discounts or taxes are applied. + public var amountSubtotal: Int? + /// Total after discounts and taxes. + public var amountTotal: Int? + /// Three-letter ISO currency code, in lowercase. Must be a supported currency. + public var currency: StripeCurrency? + /// An arbitrary string attached to the object. Often useful for displaying to users. Defaults to product name. + public var description: String? + /// This field is not included by default. To include it in the response, expand the `discounts` field. + public var discounts: [StripeQuoteLineItemDiscount]? + /// The price used to generate the line item. + public var price: StripePrice? + /// The quantity of products being purchased. + public var quantity: Int? + /// The taxes applied to the line item. + /// + /// This field is not included by default. To include it in the response, expand the `taxes` field. + public var taxes: [StripeQuoteLineItemTax]? +} + +public struct StripeQuoteLineItemDiscount: StripeModel { + /// The amount discounted. + public var amount: Int? + /// The discount applied. + public var discount: StripeDiscount? +} + +public struct StripeQuoteLineItemTax: StripeModel { + /// Amount of tax applied for this rate. + public var amount: Int? + /// The tax rate applied. + public var rate: StripeTaxRate? +} + +public struct StripeQuoteLineItemList: StripeModel { + public var object: String + public var hasMore: Bool? + public var url: String? + public var data: [StripeQuoteLineItem]? +} diff --git a/Sources/StripeKit/Billing/Quote Line Items/QuoteLineItemRoutes.swift b/Sources/StripeKit/Billing/Quote Line Items/QuoteLineItemRoutes.swift new file mode 100644 index 00000000..71c568fc --- /dev/null +++ b/Sources/StripeKit/Billing/Quote Line Items/QuoteLineItemRoutes.swift @@ -0,0 +1,65 @@ +// +// QuoteLineItemRoutes.swift +// StripeKit +// +// Created by Andrew Edwards on 7/25/21. +// + +import NIO +import NIOHTTP1 + +public protocol QuoteLineItemRoutes { + /// When retrieving a quote, there is an includable `line_items` property containing the first handful of those items. There is also a URL where you can retrieve the full (paginated) list of line items. + /// - Parameter quote: The ID of the quote + /// - Parameter filter: A dictionary that will be used for the query parameters. + /// - Returns: A `StripeQuoteLineItemList`. + func retrieve(quote: String, filter: [String: Any]?) -> EventLoopFuture + + /// When retrieving a quote, there is an includable `upfront.line_items` property containing the first handful of those items. There is also a URL where you can retrieve the full (paginated) list of upfront line items. + /// - Parameter quote: The ID of the quote + /// - Parameter filter: A dictionary that will be used for the query parameters. + /// - Returns: A `StripeQuoteLineItemList`. + func retrieveUpfront(quote: String, filter: [String: Any]?) -> EventLoopFuture + + /// Headers to send with the request. + var headers: HTTPHeaders { get set } +} + +extension QuoteLineItemRoutes { + public func retrieve(quote: String, filter: [String: Any]? = nil) -> EventLoopFuture { + retrieve(quote: quote, filter: filter) + } + + public func retrieveUpfront(quote: String, filter: [String: Any]? = nil) -> EventLoopFuture { + retrieveUpfront(quote: quote, filter: filter) + } +} + +public struct StripeQuoteLineItemRoutes: QuoteLineItemRoutes { + public var headers: HTTPHeaders = [:] + + private let apiHandler: StripeAPIHandler + private let quotelineitems = APIBase + APIVersion + "quotes" + + init(apiHandler: StripeAPIHandler) { + self.apiHandler = apiHandler + } + + public func retrieve(quote: String, filter: [String: Any]?) -> EventLoopFuture { + var queryParams = "" + if let filter = filter { + queryParams = filter.queryParameters + } + + return apiHandler.send(method: .GET, path: "\(quotelineitems)/\(quote)/line_items", query: queryParams, headers: headers) + } + + public func retrieveUpfront(quote: String, filter: [String: Any]?) -> EventLoopFuture { + var queryParams = "" + if let filter = filter { + queryParams = filter.queryParameters + } + + return apiHandler.send(method: .GET, path: "\(quotelineitems)/\(quote)/computed_upfront_line_items", query: queryParams, headers: headers) + } +} diff --git a/Sources/StripeKit/Billing/Quotes/Quote.swift b/Sources/StripeKit/Billing/Quotes/Quote.swift new file mode 100644 index 00000000..acdeae36 --- /dev/null +++ b/Sources/StripeKit/Billing/Quotes/Quote.swift @@ -0,0 +1,286 @@ +// +// Quote.swift +// StripeKit +// +// Created by Andrew Edwards on 7/25/21. +// + +import Foundation + +/// A Quote is a way to model prices that you'd like to provide to a customer. Once accepted, it will automatically create an invoice, subscription or subscription schedule. +public struct StripeQuote: StripeModel { + /// Unique identifier for the object. + public var id: String + /// String representing the object’s type. Objects of the same type share the same value. + public var object: String + /// This field is not included by default. To include it in the response, expand the `line_items` field. + public var lineItems: StripeQuoteLineItemList? + /// Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. + public var metadata: [String: String]? + /// Total before any discounts or taxes are applied. + public var amountSubtotal: Int? + /// Total after discounts and taxes are applied. + public var amountTotal: Int? + /// The amount of the application fee (if any) that will be requested to be applied to the payment and transferred to the application owner’s Stripe account. Only applicable if there are no line items with recurring prices on the quote. + public var applicationFeeAmount: Int? + /// A non-negative decimal between 0 and 100, with at most two decimal places. This represents the percentage of the subscription invoice subtotal that will be transferred to the application owner’s Stripe account. Only applicable if there are line items with recurring prices on the quote. + public var applicationFeePercent: String? + /// Settings for automatic tax lookup for this quote and resulting invoices and subscriptions. + public var automaticTax: StripeQuoteAutomaticTax? + /// Either `charge_automatically`, or `send_invoice`. When charging automatically, Stripe will attempt to pay invoices at the end of the subscription cycle or on finalization using the default payment method attached to the subscription or customer. When sending an invoice, Stripe will email your customer an invoice with payment instructions. Defaults to `charge_automatically`. + public var collectionMethod: String? + /// The definitive totals and line items for the quote, computed based on your inputted line items as well as other configuration such as trials. Used for rendering the quote to your customer. + public var computed: StripeQuoteComputed? + /// Time at which the object was created. Measured in seconds since the Unix epoch. + public var created: Date + /// Three-letter ISO currency code, in lowercase. Must be a supported currency. + public var currency: StripeCurrency? + /// The tax rates applied to this quote. + public var defaultTaxRates: [String]? + /// A description that will be displayed on the quote PDF. + public var description: String? + /// The discounts applied to this quote. + public var discounts: [String]? + /// The date on which the quote will be canceled if in `open` or `draft` status. Measured in seconds since the Unix epoch. + public var expiresAt: Date? + /// A footer that will be displayed on the quote PDF. + public var footer: String? + /// Details of the quote that was cloned. See the cloning documentation for more details. + public var fromQuote: StripeQuoteFromQuote? + /// A header that will be displayed on the quote PDF. + public var header: String? + @Expandable public var invoice: String? + /// All invoices will be billed using the specified settings. + public var invoiceSettings: StripeQuoteInvoiceSettings? + /// Has the value `true` if the object exists in live mode or the value `false` if the object exists in test mode. + public var livemode: Bool + /// A unique number that identifies this particular quote. This number is assigned once the quote is finalized. + public var number: String? + /// The account on behalf of which to charge. See the Connect documentation for details. + @Expandable public var onBehalfOf: String? + /// The status of the quote. + public var status: StripeQuoteStatus? + /// The timestamps of which the quote transitioned to a new status. + public var statusTransitions: StripeQuoteStatusTransition? + /// The subscription that was created or updated from this quote. + @Expandable public var subscription: String? + /// When creating a subscription or subscription schedule, the specified configuration data will be used. There must be at least one line item with a recurring price for a subscription or subscription schedule to be created. + public var subscriptionData: StripeQuoteSubscriptionData? + /// The subscription schedule that was created or updated from this quote. + @Expandable public var subscriptionSchedule: String? + /// Tax and discount details for the computed total amount. + public var totalDetails: StripeQuoteTotalDetails? + /// The account (if any) the payments will be attributed to for tax reporting, and where funds from each payment will be transferred to for each of the invoices. + public var transferData: StripeQuoteTransferData? +} + +public struct StripeQuoteAutomaticTax: StripeModel { + /// Automatically calculate taxes + public var enabled: Bool + /// The status of the most recent automated tax calculation for this quote. + public var status: StripeQuoteAutomaticTaxStatus? +} + +public enum StripeQuoteAutomaticTaxStatus: String, StripeModel { + /// The location details supplied on the customer aren’t valid or don’t provide enough location information to accurately determine tax rates for the customer. + case requiresLocationInputs = "requires_location_inputs" + /// Stripe successfully calculated tax automatically on this quote. + case complete + /// The Stripe Tax service failed, please try again later. + case failed +} + +public enum StripeQuoteCollectionMethod: String, StripeModel { + case chargeAutomatically = "charge_automatically" + case sendInvoice = "send_invoice" +} + +public struct StripeQuoteComputed: StripeModel { + /// The definitive totals and line items the customer will be charged on a recurring basis. Takes into account the line items with recurring prices and discounts with `duration=forever` coupons only. Defaults to null if no inputted line items with recurring prices. + public var recurring: StripeQuoteComputedRecurring? + /// The definitive upfront totals and line items the customer will be charged on the first invoice. + public var upfront: StripeQuoteComputedUpfront? +} + +public struct StripeQuoteComputedRecurring: StripeModel { + /// Total before any discounts or taxes are applied. + public var amountSubtotal: Int? + /// Total after discounts and taxes are applied. + public var amountTotal: Int? + /// The frequency at which a subscription is billed. One of `day`, `week`, `month` or `year`. + public var interval: StripePlanInterval? + /// The number of intervals (specified in the `interval` attribute) between subscription billings. For example, `interval=month` and `interval_count=3` bills every 3 months. + public var intervalCount: Int? + /// Tax and discount details for the computed total amount. + public var totalDetails: StripeQuoteComputedRecurringTotalDetails? +} + +public struct StripeQuoteComputedRecurringTotalDetails: StripeModel { + /// This is the sum of all the line item discounts. + public var amountDiscount: Int? + /// This is the sum of all the line item shipping amounts. + public var amountShipping: Int? + /// This is the sum of all the line item tax amounts. + public var amountTax: Int? + /// Breakdown of individual tax and discount amounts that add up to the totals. + /// + /// This field is not included by default. To include it in the response, expand the `breakdown` field. + public var breakdown: StripeQuoteComputedRecurringTotalDetailsBreakdown? +} + +public struct StripeQuoteComputedRecurringTotalDetailsBreakdown: StripeModel { + /// The aggregated line item discounts. + public var discounts: [StripeQuoteComputedRecurringTotalDetailsBreakdownDiscount]? + /// The aggregated line item tax amounts by rate. + public var taxes: [StripeQuoteComputedRecurringTotalDetailsBreakdownTax]? +} + +public struct StripeQuoteComputedRecurringTotalDetailsBreakdownDiscount: StripeModel { + /// The amount discounted. + public var amount: Int? + /// The discount applied. + public var discount: StripeDiscount? +} + +public struct StripeQuoteComputedRecurringTotalDetailsBreakdownTax: StripeModel { + /// Amount of tax applied for this rate. + public var amount: Int? + /// The tax rate applied. + public var rate: StripeTaxRate? +} + +public struct StripeQuoteComputedUpfront: StripeModel { + /// Total before any discounts or taxes are applied. + public var amountSubtotal: Int? + /// Total after discounts and taxes are applied. + public var amountTotal: Int? + /// The line items that will appear on the next invoice after this quote is accepted. This does not include pending invoice items that exist on the customer but may still be included in the next invoice. + /// + /// This field is not included by default. To include it in the response, expand the `line_items` field. + public var lineItems: StripeQuoteLineItemList? + /// Tax and discount details for the computed total amount. + public var totalDetails: StripeQuoteComputedUpfrontTotalDetails? +} + +public struct StripeQuoteComputedUpfrontTotalDetails: StripeModel { + /// This is the sum of all the line item discounts. + public var amountDiscount: Int? + /// This is the sum of all the line item shipping amounts. + public var amountShipping: Int? + /// This is the sum of all the line item tax amounts. + public var amountTax: Int? + /// Breakdown of individual tax and discount amounts that add up to the totals. + /// + /// This field is not included by default. To include it in the response, expand the `breakdown` field. + public var breakdown: StripeQuoteComputedUpfrontTotalDetailsBreakdown? +} + +public struct StripeQuoteComputedUpfrontTotalDetailsBreakdown: StripeModel { + /// The aggregated line item discounts. + public var discounts: [StripeQuoteComputedUpfrontTotalDetailsBreakdownDiscount]? + /// The aggregated line item tax amounts by rate. + public var taxes: [StripeQuoteComputedUpfrontTotalDetailsBreakdownTax]? +} + +public struct StripeQuoteComputedUpfrontTotalDetailsBreakdownDiscount: StripeModel { + /// The amount discounted. + public var amount: Int? + /// The discount applied. + public var discount: StripeDiscount? +} + +public struct StripeQuoteComputedUpfrontTotalDetailsBreakdownTax: StripeModel { + /// Amount of tax applied for this rate. + public var amount: Int? + /// The tax rate applied. + public var rate: StripeTaxRate? +} + +public struct StripeQuoteFromQuote: StripeModel { + /// Whether this quote is a revision of a different quote. + public var isRevision: Bool? + /// The quote that was cloned. + @Expandable public var quote: String? +} + +public struct StripeQuoteInvoiceSettings: StripeModel { + /// Number of days within which a customer must pay invoices generated by this quote. This value will be null for quotes where `collection_method=charge_automatically`. + public var daysUntilDue: Int? +} + +public enum StripeQuoteStatus: String, StripeModel { + /// The quote can be edited while in this status and has not been sent to the customer. + case draft + /// The quote has been finalized and is awaiting action from the customer. + case open + /// The customer has accepted the quote and invoice, subscription or subscription schedule has been created. + case accepted + /// The quote has been canceled and is no longer valid. + case canceled +} + +public struct StripeQuoteStatusTransition: StripeModel { + /// The time that the quote was accepted. Measured in seconds since Unix epoch. + public var acceptedAt: Date? + /// The time that the quote was canceled. Measured in seconds since Unix epoch. + public var canceledAt: Date? + /// The time that the quote was finalized. Measured in seconds since Unix epoch. + public var finalizedAt: Date? +} + +public struct StripeQuoteSubscriptionData: StripeModel { + /// When creating a new subscription, the date of which the subscription schedule will start after the quote is accepted. This date is ignored if it is in the past when the quote is accepted. Measured in seconds since the Unix epoch. + public var effectiveDate: Date? + /// Integer representing the number of trial period days before the customer is charged for the first time. + public var trialPeriodDays: Int? +} + +public struct StripeQuoteTotalDetails: StripeModel { + /// This is the sum of all the line item discounts. + public var amountDiscount: Int? + /// This is the sum of all the line item shipping amounts. + public var amountShipping: Int? + /// This is the sum of all the line item tax amounts. + public var amountTax: Int? + /// Breakdown of individual tax and discount amounts that add up to the totals. + /// + /// This field is not included by default. To include it in the response, expand the `breakdown` field. + public var breakdown: StripeQuoteTotalDetailsBreakdown? +} + +public struct StripeQuoteTotalDetailsBreakdown: StripeModel { + /// The aggregated line item discounts. + public var discounts: [StripeQuoteTotalDetailsBreakdownDiscount]? + /// The aggregated line item tax amounts by rate. + public var taxes: [StripeQuoteTotalDetailsBreakdownTax]? +} + +public struct StripeQuoteTotalDetailsBreakdownDiscount: StripeModel { + /// The amount discounted. + public var amount: Int? + /// The discount applied. + public var discount: StripeDiscount? +} + +public struct StripeQuoteTotalDetailsBreakdownTax: StripeModel { + /// Amount of tax applied for this rate. + public var amount: Int? + /// The tax rate applied. + public var rate: StripeTaxRate? +} + +public struct StripeQuoteTransferData: StripeModel { + /// The amount in cents that will be transferred to the destination account when the invoice is paid. By default, the entire amount is transferred to the destination. + public var amount: Int? + /// A non-negative decimal between 0 and 100, with at most two decimal places. This represents the percentage of the subscription invoice subtotal that will be transferred to the destination account. By default, the entire amount will be transferred to the destination. + public var amountPercent: String? + /// The account where funds from the payment will be transferred to upon payment success. + @Expandable public var destination: String? +} + +public struct StripeQuoteList: StripeModel { + public var object: String + public var hasMore: Bool? + public var url: String? + public var data: [StripeQuoteLineItem]? +} diff --git a/Sources/StripeKit/Billing/Quotes/QuoteRoutes.swift b/Sources/StripeKit/Billing/Quotes/QuoteRoutes.swift new file mode 100644 index 00000000..362e76dc --- /dev/null +++ b/Sources/StripeKit/Billing/Quotes/QuoteRoutes.swift @@ -0,0 +1,505 @@ +// +// QuoteRoutes.swift +// File +// +// Created by Andrew Edwards on 7/31/21. +// + +import NIO +import NIOHTTP1 +import Foundation + +public protocol QuoteRoutes { + + /// A quote models prices and services for a customer. Default options for `header`, `description`, `footer`, and `expires_at` can be set in the dashboard via the quote template. + /// - Parameters: + /// - applicationFeeAmount: The amount of the application fee (if any) that will be requested to be applied to the payment and transferred to the application owner’s Stripe account. There cannot be any line items with recurring prices when using this field. + /// - applicationFeePercent: A non-negative decimal between 0 and 100, with at most two decimal places. This represents the percentage of the subscription invoice subtotal that will be transferred to the application owner’s Stripe account. There must be at least 1 line item with a recurring price to use this field. + /// - automaticTax: Settings for automatic tax lookup for this quote and resulting invoices and subscriptions. + /// - collectionMethod: Either `charge_automatically`, or `send_invoice`. When charging automatically, Stripe will attempt to pay invoices at the end of the subscription cycle or at invoice finalization using the default payment method attached to the subscription or customer. When sending an invoice, Stripe will email your customer an invoice with payment instructions. Defaults to `charge_automatically`. + /// - customer: The customer for which this quote belongs to. A customer is required before finalizing the quote. Once specified, it cannot be changed. + /// - defaultTaxRates: The tax rates that will apply to any line item that does not have `tax_rates` set. + /// - description: A description that will be displayed on the quote PDF. If no value is passed, the default description configured in your quote template settings will be used. + /// - discounts: The discounts applied to the quote. You can only set up to one discount. + /// - expiresAt: A future timestamp on which the quote will be canceled if in `open` or `draft` status. Measured in seconds since the Unix epoch. If no value is passed, the default expiration date configured in your quote template settings will be used. + /// - footer: A footer that will be displayed on the quote PDF. If no value is passed, the default footer configured in your quote template settings will be used. + /// - fromQuote: Clone an existing quote. The new quote will be created in `status=draft`. When using this parameter, you cannot specify any other parameters except for `expires_at`. + /// - header: A header that will be displayed on the quote PDF. If no value is passed, the default header configured in your quote template settings will be used. + /// - invoiceSettings: All invoices will be billed using the specified settings. + /// - lineItems: A list of line items the customer is being quoted for. Each line item includes information about the product, the quantity, and the resulting cost. + /// - metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata. + /// - onBehalfOf: The account on behalf of which to charge. + /// - subscriptionData: When creating a subscription or subscription schedule, the specified configuration data will be used. There must be at least one line item with a recurring price for a subscription or subscription schedule to be created. A subscription schedule is created if `subscription_data[effective_date]` is present and in the future, otherwise a subscription is created. + /// - transferData: The data with which to automatically create a Transfer for each of the invoices. + /// - expand: An array of properties to expand. + /// - Returns: A `StripeQuote`. + func create(applicationFeeAmount: Int?, + applicationFeePercent: String?, + automaticTax: [String: Any]?, + collectionMethod: StripeQuoteCollectionMethod?, + customer: String?, + defaultTaxRates: [String]?, + description: String?, + discounts: [[String: Any]]?, + expiresAt: Date?, + footer: String?, + fromQuote: [String: Any]?, + header: String?, + invoiceSettings: [String: Any]?, + lineItems: [[String: Any]]?, + metadata: [String: String]?, + onBehalfOf: String?, + subscriptionData: [String: Any]?, + transferData: [String: Any]?, + expand: [String]?) -> EventLoopFuture + + /// Retrieves the quote with the given ID. + /// - Parameter quote: The id of the quote. + /// - Parameter expand: An array of properties to expand. + /// - Returns: A `StripeQuote`. + func retrieve(quote: String, expand: [String]?) -> EventLoopFuture + + /// A quote models prices and services for a customer. + /// - Parameters: + /// - quote: The id of the quote. + /// - applicationFeeAmount: The amount of the application fee (if any) that will be requested to be applied to the payment and transferred to the application owner’s Stripe account. There cannot be any line items with recurring prices when using this field. + /// - applicationFeePercent: A non-negative decimal between 0 and 100, with at most two decimal places. This represents the percentage of the subscription invoice subtotal that will be transferred to the application owner’s Stripe account. There must be at least 1 line item with a recurring price to use this field. + /// - automaticTax: Settings for automatic tax lookup for this quote and resulting invoices and subscriptions. + /// - collectionMethod: Either `charge_automatically`, or `send_invoice`. When charging automatically, Stripe will attempt to pay invoices at the end of the subscription cycle or at invoice finalization using the default payment method attached to the subscription or customer. When sending an invoice, Stripe will email your customer an invoice with payment instructions. Defaults to `charge_automatically`. + /// - customer: The customer for which this quote belongs to. A customer is required before finalizing the quote. Once specified, it cannot be changed. + /// - defaultTaxRates: The tax rates that will apply to any line item that does not have `tax_rates` set. + /// - description: A description that will be displayed on the quote PDF. If no value is passed, the default description configured in your quote template settings will be used. + /// - discounts: The discounts applied to the quote. You can only set up to one discount. + /// - expiresAt: A future timestamp on which the quote will be canceled if in `open` or `draft` status. Measured in seconds since the Unix epoch. If no value is passed, the default expiration date configured in your quote template settings will be used. + /// - footer: A footer that will be displayed on the quote PDF. If no value is passed, the default footer configured in your quote template settings will be used. + /// - header: A header that will be displayed on the quote PDF. If no value is passed, the default header configured in your quote template settings will be used. + /// - invoiceSettings: All invoices will be billed using the specified settings. + /// - lineItems: A list of line items the customer is being quoted for. Each line item includes information about the product, the quantity, and the resulting cost. + /// - metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata. + /// - onBehalfOf: The account on behalf of which to charge. + /// - subscriptionData: When creating a subscription or subscription schedule, the specified configuration data will be used. There must be at least one line item with a recurring price for a subscription or subscription schedule to be created. A subscription schedule is created if `subscription_data[effective_date]` is present and in the future, otherwise a subscription is created. + /// - transferData: The data with which to automatically create a Transfer for each of the invoices. + /// - expand: An array of properties to expand. + /// - Returns: A `StripeQuote`. + func update(quote: String, + applicationFeeAmount: Int?, + applicationFeePercent: String?, + automaticTax: [String: Any]?, + collectionMethod: StripeQuoteCollectionMethod?, + customer: String?, + defaultTaxRates: [String]?, + description: String?, + discounts: [[String: Any]]?, + expiresAt: Date?, + footer: String?, + header: String?, + invoiceSettings: [String: Any]?, + lineItems: [[String: Any]]?, + metadata: [String: String]?, + onBehalfOf: String?, + subscriptionData: [String: Any]?, + transferData: [String: Any]?, + expand: [String]?) -> EventLoopFuture + + /// Finalizes the quote. + /// - Parameter quote: The id of the quote + /// - Parameter expand: An array of properties to expand. + /// - Returns: A `StripeQuote`. + func finalize(quote: String, + expiresAt: Date?, + expand: [String]?) -> EventLoopFuture + + /// Accepts the specified quote. + /// - Parameter quote: The id of the quote + /// - Parameter expand: An array of properties to expand. + /// - Returns: A `StripeQuote`. + func accept(quote: String, expand: [String]?) -> EventLoopFuture + + /// Cancels the quote. + /// - Parameter quote: The id of the quote + /// - Parameter expand: An array of properties to expand. + /// - Returns: A `StripeQuote`. + func cancel(quote: String, expand: [String]?) -> EventLoopFuture + + /// Download the PDF for a finalized quote + /// - Parameter quote: The id of the quote + /// - Returns: The pdf data in `Data`. + func downloadPDF(quote: String) -> EventLoopFuture + + /// Returns a list of your quotes. + /// - Parameter filter: A dictionary that will be used for the query parameters. + /// - Returns: A `StripeQuoteList`. + func listAll(filter: [String: Any]?) -> EventLoopFuture + + /// Headers to send with the request. + var headers: HTTPHeaders { get set } +} + +extension QuoteRoutes { + public func create(applicationFeeAmount: Int? = nil, + applicationFeePercent: String? = nil, + automaticTax: [String: Any]? = nil, + collectionMethod: StripeQuoteCollectionMethod? = nil, + customer: String? = nil, + defaultTaxRates: [String]? = nil, + description: String? = nil, + discounts: [[String: Any]]? = nil, + expiresAt: Date? = nil, + footer: String? = nil, + fromQuote: [String: Any]? = nil, + header: String? = nil, + invoiceSettings: [String: Any]? = nil, + lineItems: [[String: Any]]? = nil, + metadata: [String: String]? = nil, + onBehalfOf: String? = nil, + subscriptionData: [String: Any]? = nil, + transferData: [String: Any]? = nil, + expand: [String]? = nil) -> EventLoopFuture { + create(applicationFeeAmount: applicationFeeAmount, + applicationFeePercent: applicationFeePercent, + automaticTax: automaticTax, + collectionMethod: collectionMethod, + customer: customer, + defaultTaxRates: defaultTaxRates, + description: description, + discounts: discounts, + expiresAt: expiresAt, + footer: footer, + fromQuote: fromQuote, + header: header, + invoiceSettings: invoiceSettings, + lineItems: lineItems, + metadata: metadata, + onBehalfOf: onBehalfOf, + subscriptionData: subscriptionData, + transferData: transferData, + expand: expand) + } + + public func retrieve(quote: String, expand: [String]? = nil) -> EventLoopFuture { + retrieve(quote: quote, expand: expand) + } + + public func update(quote: String, + applicationFeeAmount: Int? = nil, + applicationFeePercent: String? = nil, + automaticTax: [String: Any]? = nil, + collectionMethod: StripeQuoteCollectionMethod? = nil, + customer: String? = nil, + defaultTaxRates: [String]? = nil, + description: String? = nil, + discounts: [[String: Any]]? = nil, + expiresAt: Date? = nil, + footer: String? = nil, + header: String? = nil, + invoiceSettings: [String: Any]? = nil, + lineItems: [[String: Any]]? = nil, + metadata: [String: String]? = nil, + onBehalfOf: String? = nil, + subscriptionData: [String: Any]? = nil, + transferData: [String: Any]? = nil, + expand: [String]? = nil) -> EventLoopFuture { + update(quote: quote, + applicationFeeAmount: applicationFeeAmount, + applicationFeePercent: applicationFeePercent, + automaticTax: automaticTax, + collectionMethod: collectionMethod, + customer: customer, + defaultTaxRates: defaultTaxRates, + description: description, + discounts: discounts, + expiresAt: expiresAt, + footer: footer, + header: header, + invoiceSettings: invoiceSettings, + lineItems: lineItems, + metadata: metadata, + onBehalfOf: onBehalfOf, + subscriptionData: subscriptionData, + transferData: transferData, + expand: expand) + } + + public func finalize(quote: String, + expiresAt: Date? = nil, + expand: [String]? = nil) -> EventLoopFuture { + finalize(quote: quote, expiresAt: expiresAt, expand: expand) + } + + public func accept(quote: String, expand: [String]? = nil) -> EventLoopFuture { + accept(quote: quote, expand: expand) + } + + public func cancel(quote: String, expand: [String]? = nil) -> EventLoopFuture { + cancel(quote: quote, expand: expand) + } + + public func downloadPDF(quote: String) -> EventLoopFuture { + downloadPDF(quote: quote) + } + + public func listAll(filter: [String: Any]? = nil) -> EventLoopFuture { + listAll(filter: filter) + } +} + +public struct StripeQuoteRoutes: QuoteRoutes { + public var headers: HTTPHeaders = [:] + + private let apiHandler: StripeAPIHandler + private let quotes = APIBase + APIVersion + "quotes" + + init(apiHandler: StripeAPIHandler) { + self.apiHandler = apiHandler + } + + public func create(applicationFeeAmount: Int?, + applicationFeePercent: String?, + automaticTax: [String: Any]?, + collectionMethod: StripeQuoteCollectionMethod?, + customer: String?, + defaultTaxRates: [String]?, + description: String?, + discounts: [[String: Any]]?, + expiresAt: Date?, + footer: String?, + fromQuote: [String: Any]?, + header: String?, + invoiceSettings: [String: Any]?, + lineItems: [[String: Any]]?, + metadata: [String: String]?, + onBehalfOf: String?, + subscriptionData: [String: Any]?, + transferData: [String: Any]?, + expand: [String]?) -> EventLoopFuture { + var body: [String: Any] = [:] + + if let applicationFeeAmount = applicationFeeAmount { + body["application_fee_amount"] = applicationFeeAmount + } + + if let applicationFeePercent = applicationFeePercent { + body["application_fee_percent"] = applicationFeePercent + } + + if let automaticTax = automaticTax { + automaticTax.forEach { body["automatic_tax[\($0)]"] = $1 } + } + + if let collectionMethod = collectionMethod { + body["collection_method"] = collectionMethod.rawValue + } + + if let customer = customer { + body["customer"] = customer + } + + if let defaultTaxRates = defaultTaxRates { + body["default_tax_rates"] = defaultTaxRates + } + + if let description = description { + body["description"] = description + } + + if let discounts = discounts { + body["discounts"] = discounts + } + + if let expiresAt = expiresAt { + body["expires_at"] = Int(expiresAt.timeIntervalSince1970) + } + + if let footer = footer { + body["footer"] = footer + } + + if let fromQuote = fromQuote { + fromQuote.forEach { body["from_quote[\($0)]"] = $1 } + } + + if let header = header { + body["header"] = header + } + + if let invoiceSettings = invoiceSettings { + invoiceSettings.forEach { body["invoice_settings[\($0)]"] = $1 } + } + + if let lineItems = lineItems { + body["line_items"] = lineItems + } + + if let metadata = metadata { + metadata.forEach { body["metadata[\($0)]"] = $1 } + } + + if let onBehalfOf = onBehalfOf { + body["on_behalf_of"] = onBehalfOf + } + + if let subscriptionData = subscriptionData { + subscriptionData.forEach { body["subscription_data[\($0)]"] = $1 } + } + + if let transferData = transferData { + transferData.forEach { body["transfer_data[\($0)]"] = $1 } + } + + if let expand = expand { + body["expand"] = expand + } + + return apiHandler.send(method: .POST, path: quotes, body: .string(body.queryParameters), headers: headers) + } + + public func retrieve(quote: String, expand: [String]?) -> EventLoopFuture { + var queryParams = "" + if let expand = expand { + queryParams += ["expand": expand].queryParameters + } + + return apiHandler.send(method: .GET, path: "\(quotes)/\(quote)", query: queryParams, headers: headers) + } + + public func update(quote: String, + applicationFeeAmount: Int?, + applicationFeePercent: String?, + automaticTax: [String: Any]?, + collectionMethod: StripeQuoteCollectionMethod?, + customer: String?, + defaultTaxRates: [String]?, + description: String?, + discounts: [[String: Any]]?, + expiresAt: Date?, + footer: String?, + header: String?, + invoiceSettings: [String: Any]?, + lineItems: [[String: Any]]?, + metadata: [String: String]?, + onBehalfOf: String?, + subscriptionData: [String: Any]?, + transferData: [String: Any]?, + expand: [String]?) -> EventLoopFuture { + var body: [String: Any] = [:] + + if let applicationFeeAmount = applicationFeeAmount { + body["application_fee_amount"] = applicationFeeAmount + } + + if let applicationFeePercent = applicationFeePercent { + body["application_fee_percent"] = applicationFeePercent + } + + if let automaticTax = automaticTax { + automaticTax.forEach { body["automatic_tax[\($0)]"] = $1 } + } + + if let collectionMethod = collectionMethod { + body["collection_method"] = collectionMethod.rawValue + } + + if let customer = customer { + body["customer"] = customer + } + + if let defaultTaxRates = defaultTaxRates { + body["default_tax_rates"] = defaultTaxRates + } + + if let description = description { + body["description"] = description + } + + if let discounts = discounts { + body["discounts"] = discounts + } + + if let expiresAt = expiresAt { + body["expires_at"] = Int(expiresAt.timeIntervalSince1970) + } + + if let footer = footer { + body["footer"] = footer + } + + if let header = header { + body["header"] = header + } + + if let invoiceSettings = invoiceSettings { + invoiceSettings.forEach { body["invoice_settings[\($0)]"] = $1 } + } + + if let lineItems = lineItems { + body["line_items"] = lineItems + } + + if let metadata = metadata { + metadata.forEach { body["metadata[\($0)]"] = $1 } + } + + if let onBehalfOf = onBehalfOf { + body["on_behalf_of"] = onBehalfOf + } + + if let subscriptionData = subscriptionData { + subscriptionData.forEach { body["subscription_data[\($0)]"] = $1 } + } + + if let transferData = transferData { + transferData.forEach { body["transfer_data[\($0)]"] = $1 } + } + + if let expand = expand { + body["expand"] = expand + } + + return apiHandler.send(method: .POST, path: "\(quotes)/\(quote)", body: .string(body.queryParameters), headers: headers) + } + + public func finalize(quote: String, expiresAt: Date?, expand: [String]?) -> EventLoopFuture { + var body: [String: Any] = [:] + + if let expiresAt = expiresAt { + body["expires_at"] = Int(expiresAt.timeIntervalSince1970) + } + + if let expand = expand { + body["expand"] = expand + } + + return apiHandler.send(method: .POST, path: "\(quotes)/\(quote)/finalize", body: .string(body.queryParameters), headers: headers) + } + + public func accept(quote: String, expand: [String]?) -> EventLoopFuture { + var queryParams = "" + if let expand = expand { + queryParams += ["expand": expand].queryParameters + } + + return apiHandler.send(method: .POST, path: "\(quotes)/\(quote)/accept", query: queryParams, headers: headers) + } + + public func cancel(quote: String, expand: [String]?) -> EventLoopFuture { + var queryParams = "" + if let expand = expand { + queryParams += ["expand": expand].queryParameters + } + + return apiHandler.send(method: .POST, path: "\(quotes)/\(quote)/cancel", query: queryParams, headers: headers) + } + + public func downloadPDF(quote: String) -> EventLoopFuture { + apiHandler.send(method: .GET, path: "\(quotes)/\(quote)", headers: headers) + } + + public func listAll(filter: [String: Any]?) -> EventLoopFuture { + var queryParams = "" + if let filter = filter { + queryParams += filter.queryParameters + } + + return apiHandler.send(method: .GET, path: quotes, query: queryParams, headers: headers) + } +} diff --git a/Sources/StripeKit/Billing/Subscription Items/SubscriptionItem.swift b/Sources/StripeKit/Billing/Subscription Items/SubscriptionItem.swift index 8fc5f79f..0ad7cdbd 100644 --- a/Sources/StripeKit/Billing/Subscription Items/SubscriptionItem.swift +++ b/Sources/StripeKit/Billing/Subscription Items/SubscriptionItem.swift @@ -44,6 +44,9 @@ public enum StripeSubscriptionItemPaymentBehavior: String, StripeModel { case errorIfIncomplete = "error_if_incomplete" /// Use `pending_if_incomplete` to update the subscription using pending updates. When you use `pending_if_incomplete` you can only pass the parameters supported by pending updates. case pendingIfIncomplete = "pending_if_incomplete" + /// Use `default_incomplete` to transition the subscription to `status=past_due` when payment is required and await explicit confirmation of the invoice’s payment intent. This allows simpler management of scenarios where additional user actions are needed to pay a subscription’s invoice. Such as failed payments, SCA regulation, or collecting a mandate for a bank debit payment method. + case defaultIncomplete = "default_incomplete" + } public enum StripeSubscriptionItemProrationBehavior: String, StripeModel { diff --git a/Sources/StripeKit/Billing/Subscriptions/Subscription.swift b/Sources/StripeKit/Billing/Subscriptions/Subscription.swift index f6d1b54c..a1906eec 100644 --- a/Sources/StripeKit/Billing/Subscriptions/Subscription.swift +++ b/Sources/StripeKit/Billing/Subscriptions/Subscription.swift @@ -42,7 +42,7 @@ public struct StripeSubscription: StripeModel { @Expandable public var defaultPaymentMethod: String? /// ID of the default payment source for the subscription. It must belong to the customer associated with the subscription and be in a chargeable state. If not set, defaults to the customer’s default source. @Expandable public var defaultSource: String? - /// The tax rates that will apply to any subscription item that does not have tax_rates set. Invoices created will have their default_tax_rates populated from the subscription. + /// The tax rates that will apply to any subscription item that does not have `tax_rates` set. Invoices created will have their `default_tax_rates` populated from the subscription. public var defaultTaxRates: [StripeTaxRate]? /// Describes the current discount applied to this subscription, if there is one. When billing, a discount applied to a subscription overrides a discount applied on a customer-wide basis. public var discount: StripeDiscount? @@ -58,10 +58,12 @@ public struct StripeSubscription: StripeModel { public var livemode: Bool? /// Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. public var metadata: [String: String]? - /// Specifies the approximate timestamp on which any pending invoice items will be billed according to the schedule provided at pending_invoice_item_interval. + /// Specifies the approximate timestamp on which any pending invoice items will be billed according to the schedule provided at `pending_invoice_item_interval`. public var nextPendingInvoiceItemInvoice: Date? /// If specified, payment collection for this subscription will be paused. public var pauseCollection: StripeSubscriptionPauseCollection? + /// Payment settings passed on to invoices created by the subscription. + public var paymentSettings: StripeSubscriptionPaymentSettings? /// Specifies an interval for how often to bill for any pending invoice items. It is analogous to calling Create an invoice for the given subscription at the specified interval. public var pendingInvoiceItemInterval: StripeSubscriptionPendingInvoiceInterval? /// You can use this SetupIntent to collect user authentication when creating a subscription without immediate payment or updating a subscription’s payment method, allowing you to optimize for off-session payments. Learn more in the SCA Migration Guide. @@ -72,7 +74,15 @@ public struct StripeSubscription: StripeModel { @Expandable public var schedule: String? /// Date when the subscription was first created. The date might differ from the `created` date due to backdating. public var startDate: Date? - /// Possible values are `incomplete`, `incomplete_expired`, `trialing`, `active`, `past_due`, `canceled`, or `unpaid`. For `collection_method=charge_automatically` a subscription moves into `incomplete` if the initial payment attempt fails. A subscription in this state can only have metadata and `default_source` updated. Once the first invoice is paid, the subscription moves into an active state. If the first invoice is not paid within 23 hours, the subscription transitions to `incomplete_expired`. This is a terminal state, the open invoice will be voided and no further invoices will be generated. A subscription that is currently in a trial period is trialing and moves to active when the trial period is over. If subscription `collection_method=charge_automatically` it becomes `past_due` when payment to renew it fails and canceled or unpaid (depending on your subscriptions settings) when Stripe has exhausted all payment retry attempts. If subscription `collection_method=send_invoice` it becomes `past_due` when its invoice is not paid by the due date, and `canceled` or `unpaid` if it is still not paid by an additional deadline after that. Note that when a subscription has a status of `unpaid`, no subsequent invoices will be attempted (invoices will be created, but then immediately automatically closed). After receiving updated payment information from a customer, you may choose to reopen and pay their closed invoices. + /// Possible values are `incomplete`, `incomplete_expired`, `trialing`, `active`, `past_due`, `canceled`, or `unpaid`. + /// + /// For `collection_method=charge_automatically` a subscription moves into `incomplete` if the initial payment attempt fails. A subscription in this state can only have metadata and `default_source` updated. Once the first invoice is paid, the subscription moves into an active state. If the first invoice is not paid within 23 hours, the subscription transitions to `incomplete_expired`. This is a terminal state, the open invoice will be voided and no further invoices will be generated. + /// + /// A subscription that is currently in a trial period is trialing and moves to active when the trial period is over. + /// + /// If subscription `collection_method=charge_automatically` it becomes `past_due` when payment to renew it fails and canceled or unpaid (depending on your subscriptions settings) when Stripe has exhausted all payment retry attempts. + /// + /// If subscription `collection_method=send_invoice` it becomes `past_due` when its invoice is not paid by the due date, and `canceled` or `unpaid` if it is still not paid by an additional deadline after that. Note that when a subscription has a status of `unpaid`, no subsequent invoices will be attempted (invoices will be created, but then immediately automatically closed). After receiving updated payment information from a customer, you may choose to reopen and pay their closed invoices. public var status: StripeSubscriptionStatus? /// The account (if any) the subscription’s payments will be attributed to for tax reporting, and where funds from each payment will be transferred to for each of the subscription’s invoices. public var transferData: StripeSubscriptionTransferData? @@ -89,6 +99,55 @@ public struct StripeSubscriptionBillingThresholds: StripeModel { public var resetBillingCycleAnchor: Bool? } +public struct StripeSubscriptionPaymentSettings: StripeModel { + /// Payment-method-specific configuration to provide to invoices created by the subscription. + public var paymentMethodOptions: StripeSubscriptionPaymentSettingsPaymentMethodOptions? + /// The list of payment method types to provide to every invoice created by the subscription. If not set, Stripe attempts to automatically determine the types to use by looking at the invoice’s default payment method, the subscription’s default payment method, the customer’s default payment method, and your invoice template settings. + public var paymentMethodTypes: [SubscriptionPaymentSettingsPaymentMethodType]? +} + +public struct StripeSubscriptionPaymentSettingsPaymentMethodOptions: StripeModel { + /// This sub-hash contains details about the Bancontact payment method options to pass to invoices created by the subscription. + public var bancontact: StripeSubscriptionPaymentSettingsPaymentMethodOptionsBancontact? + /// This sub-hash contains details about the Card payment method options to pass to invoices created by the subscription. + public var card: StripeSubscriptionPaymentSettingsPaymentMethodOptionsCard? +} + +public struct StripeSubscriptionPaymentSettingsPaymentMethodOptionsBancontact: StripeModel { + /// Preferred language of the Bancontact authorization page that the customer is redirected to. + public var preferredLanguage: String? +} + +public struct StripeSubscriptionPaymentSettingsPaymentMethodOptionsCard: StripeModel { + /// We strongly recommend that you rely on our SCA Engine to automatically prompt your customers for authentication based on risk level and other requirements. However, if you wish to request 3D Secure based on logic from your own fraud engine, provide this option. Read our guide on manually requesting 3D Secure for more information on how this configuration interacts with Radar and our SCA Engine. + public var requestThreeDSecure: StripeSubscriptionPaymentSettingsPaymentMethodOptionsCardRequestThreedSecure? +} + +public enum StripeSubscriptionPaymentSettingsPaymentMethodOptionsCardRequestThreedSecure: String, StripeModel { + /// Triggers 3D Secure authentication only if it is required. + case automatic + /// Requires 3D Secure authentication if it is available. + case any +} + +public enum SubscriptionPaymentSettingsPaymentMethodType: String, StripeModel { + case achCreditTransfer = "ach_transfer_credit" + case achDebit = "ach_debit" + case auBecsDebit = "au_becs_debit" + case bacsDebit = "bacs_debit" + case bancontact + case boleto + case card + case eps + case fpx + case giropay + case ideal + case p24 + case sepaDebit = "sepa_debit" + case sofort + case wechatPay = "wechat_pay" +} + public struct StripeSubscriptionPendingInvoiceInterval: StripeModel { /// Specifies invoicing frequency. Either `day`, `week`, `month` or `year`. public var interval: StripePlanInterval? @@ -125,6 +184,8 @@ public enum StripeSubscriptionPaymentBehavior: String, StripeModel { case errorIfIncomplete = "error_if_incomplete" /// Use `pending_if_incomplete` to update the subscription using pending updates. When you use `pending_if_incomplete` you can only pass the parameters supported by pending updates. case pendingIfIncomplete = "pending_if_incomplete" + /// Use `default_incomplete` to create Subscriptions with `status=incomplete` when the first invoice requires payment, otherwise start as active. Subscriptions transition to `status=active` when successfully confirming the payment intent on the first invoice. This allows simpler management of scenarios where additional user actions are needed to pay a subscription’s invoice. Such as failed payments, SCA regulation, or collecting a mandate for a bank debit payment method. If the payment intent is not confirmed within 23 hours subscriptions transition to `status=incomplete_expired`, which is a terminal state. + case defaultIncomplete = "default_incomplete" } public enum StripeSubscriptionProrationBehavior: String, StripeModel { diff --git a/Sources/StripeKit/Billing/Subscriptions/SubscriptionRoutes.swift b/Sources/StripeKit/Billing/Subscriptions/SubscriptionRoutes.swift index 43773975..16a43fc9 100644 --- a/Sources/StripeKit/Billing/Subscriptions/SubscriptionRoutes.swift +++ b/Sources/StripeKit/Billing/Subscriptions/SubscriptionRoutes.swift @@ -31,6 +31,7 @@ public protocol SubscriptionRoutes { /// - items: List of subscription items, each with an attached plan. /// - metadata: A set of key-value pairs that you can attach to a Subscription object. It can be useful for storing additional information about the subscription in a structured format. /// - offSession: Indicates if a customer is on or off-session while an invoice payment is attempted. + /// - paymentSettings: Payment settings to pass to invoices created by the subscription. /// - paymentBehavior: Use `allow_incomplete` to create subscriptions with `status=incomplete` if its first invoice cannot be paid. Creating subscriptions with this status allows you to manage scenarios where additional user actions are needed to pay a subscription’s invoice. For example, SCA regulation may require 3DS authentication to complete payment. See the [SCA Migration Guide](https://stripe.com/docs/billing/migration/strong-customer-authentication) for Billing to learn more. This is the default behavior. Use `error_if_incomplete` if you want Stripe to return an HTTP 402 status code if a subscription’s first invoice cannot be paid. For example, if a payment method requires 3DS authentication due to SCA regulation and further user action is needed, this parameter does not create a subscription and returns an error instead. This was the default behavior for API versions prior to 2019-03-14. See the [changelog](https://stripe.com/docs/upgrades#2019-03-14) to learn more. `pending_if_incomplete` is only used with updates and cannot be passed when creating a subscription. /// - pendingInvoiceItemInterval: Specifies an interval for how often to bill for any pending invoice items. It is analogous to calling [Create an invoice](https://stripe.com/docs/api#create_invoice) for the given subscription at the specified interval. /// - promotionCode: The API ID of a promotion code to apply to the customer. The customer will have a discount applied on all recurring payments. Charges you create through the API will not have the discount. @@ -58,6 +59,7 @@ public protocol SubscriptionRoutes { items: [[String: Any]], metadata: [String: String]?, offSession: Bool?, + paymentSettings: [String: Any]?, paymentBehavior: StripeSubscriptionPaymentBehavior?, pendingInvoiceItemInterval: [String: Any]?, promotionCode: String?, @@ -95,6 +97,7 @@ public protocol SubscriptionRoutes { /// - items: List of subscription items, each with an attached plan. /// - metadata: A set of key-value pairs that you can attach to a subscription object. This can be useful for storing additional information about the subscription in a structured format. /// - offSession: Indicates if a customer is on or off-session while an invoice payment is attempted. + /// - paymentSettings: Payment settings to pass to invoices created by the subscription. /// - pauseCollection: If specified, payment collection for this subscription will be paused. /// - paymentBehavior: Use `allow_incomplete` to create subscriptions with `status=incomplete` if its first invoice cannot be paid. Creating subscriptions with this status allows you to manage scenarios where additional user actions are needed to pay a subscription’s invoice. For example, SCA regulation may require 3DS authentication to complete payment. See the [SCA Migration Guide](https://stripe.com/docs/billing/migration/strong-customer-authentication) for Billing to learn more. This is the default behavior. Use `error_if_incomplete` if you want Stripe to return an HTTP 402 status code if a subscription’s first invoice cannot be paid. For example, if a payment method requires 3DS authentication due to SCA regulation and further user action is needed, this parameter does not create a subscription and returns an error instead. This was the default behavior for API versions prior to 2019-03-14. See the [changelog](https://stripe.com/docs/upgrades#2019-03-14) to learn more. /// - pendingInvoiceItemInterval: Specifies an interval for how often to bill for any pending invoice items. It is analogous to calling [Create an invoice](https://stripe.com/docs/api#create_invoice) for the given subscription at the specified interval. @@ -122,6 +125,7 @@ public protocol SubscriptionRoutes { items: [[String: Any]]?, metadata: [String: String]?, offSession: Bool?, + paymentSettings: [String: Any]?, pauseCollection: [String: Any]?, paymentBehavior: StripeSubscriptionPaymentBehavior?, pendingInvoiceItemInterval: [String: Any]?, @@ -175,6 +179,7 @@ extension SubscriptionRoutes { items: [[String: Any]], metadata: [String: String]? = nil, offSession: Bool? = nil, + paymentSettings: [String: Any]? = nil, paymentBehavior: StripeSubscriptionPaymentBehavior? = nil, pendingInvoiceItemInterval: [String: Any]? = nil, promotionCode: String? = nil, @@ -201,6 +206,7 @@ extension SubscriptionRoutes { items: items, metadata: metadata, offSession: offSession, + paymentSettings: paymentSettings, paymentBehavior: paymentBehavior, pendingInvoiceItemInterval: pendingInvoiceItemInterval, promotionCode: promotionCode, @@ -232,6 +238,7 @@ extension SubscriptionRoutes { items: [[String: Any]]? = nil, metadata: [String: String]? = nil, offSession: Bool? = nil, + paymentSettings: [String: Any]? = nil, pauseCollection: [String: Any]? = nil, paymentBehavior: StripeSubscriptionPaymentBehavior? = nil, pendingInvoiceItemInterval: [String: Any]? = nil, @@ -258,6 +265,7 @@ extension SubscriptionRoutes { items: items, metadata: metadata, offSession: offSession, + paymentSettings: paymentSettings, pauseCollection: pauseCollection, paymentBehavior: paymentBehavior, pendingInvoiceItemInterval: pendingInvoiceItemInterval, @@ -312,6 +320,7 @@ public struct StripeSubscriptionRoutes: SubscriptionRoutes { items: [[String: Any]], metadata: [String: String]?, offSession: Bool?, + paymentSettings: [String: Any]?, paymentBehavior: StripeSubscriptionPaymentBehavior?, pendingInvoiceItemInterval: [String: Any]?, promotionCode: String?, @@ -384,6 +393,10 @@ public struct StripeSubscriptionRoutes: SubscriptionRoutes { body["off_session"] = offSession } + if let paymentSettings = paymentSettings { + paymentSettings.forEach { body["payment_settings[\($0)]"] = $1 } + } + if let paymentBehavior = paymentBehavior { body["payment_behavior"] = paymentBehavior.rawValue } @@ -451,6 +464,7 @@ public struct StripeSubscriptionRoutes: SubscriptionRoutes { items: [[String: Any]]?, metadata: [String: String]?, offSession: Bool?, + paymentSettings: [String: Any]?, pauseCollection: [String: Any]?, paymentBehavior: StripeSubscriptionPaymentBehavior?, pendingInvoiceItemInterval: [String: Any]?, @@ -523,6 +537,10 @@ public struct StripeSubscriptionRoutes: SubscriptionRoutes { body["off_session"] = offSession } + if let paymentSettings = paymentSettings { + paymentSettings.forEach { body["payment_settings[\($0)]"] = $1 } + } + if let pauseCollection = pauseCollection { pauseCollection.forEach { body["pause_collection[\($0)]"] = $1 } } diff --git a/Sources/StripeKit/Billing/Tax Rates/TaxRate.swift b/Sources/StripeKit/Billing/Tax Rates/TaxRate.swift index 6670fa4d..bd553ed0 100644 --- a/Sources/StripeKit/Billing/Tax Rates/TaxRate.swift +++ b/Sources/StripeKit/Billing/Tax Rates/TaxRate.swift @@ -7,7 +7,7 @@ import Foundation -/// The [Tax Rate Object](https://stripe.com/docs/api/tax_rates/object). +/// The [Tax Rate Object](https://stripe.com/docs/api/tax_rates/object) public struct StripeTaxRate: StripeModel { /// Unique identifier for the object. public var id: String @@ -35,6 +35,13 @@ public struct StripeTaxRate: StripeModel { public var percentage: Decimal? /// ISO 3166-2 subdivision code, without country prefix. For example, “NY” for New York, United States. public var state: String? + /// The high-level tax type, such as `vat` or `sales_tax`. + public var taxType: StripeTaxRateTaxType? +} + +public enum StripeTaxRateTaxType: String, StripeModel { + case vat + case salesTax = "sales_tax" } public struct StripeTaxRateList: StripeModel { diff --git a/Sources/StripeKit/Billing/Tax Rates/TaxRateRoutes.swift b/Sources/StripeKit/Billing/Tax Rates/TaxRateRoutes.swift index 2f8d8659..5d631864 100644 --- a/Sources/StripeKit/Billing/Tax Rates/TaxRateRoutes.swift +++ b/Sources/StripeKit/Billing/Tax Rates/TaxRateRoutes.swift @@ -22,6 +22,7 @@ public protocol TaxRateRoutes { /// - jurisdiction: The jurisdiction for the tax rate. /// - metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to `metadata`. /// - state: ISO 3166-2 subdivision code, without country prefix. For example, “NY” for New York, United States. + /// - taxType: The high-level tax type, such as `vat` or `sales_tax`. /// - Returns: A `StripeTaxRate`. func create(displayName: String, inclusive: Bool, @@ -31,7 +32,8 @@ public protocol TaxRateRoutes { description: String?, jurisdiction: String?, metadata: [String: String]?, - state: String?) -> EventLoopFuture + state: String?, + taxType: StripeTaxRateTaxType?) -> EventLoopFuture /// Retrieves a tax rate with the given ID /// @@ -50,6 +52,7 @@ public protocol TaxRateRoutes { /// - jurisdiction: The jurisdiction for the tax rate. This will be unset if you POST an empty value. /// - metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to `metadata`. /// - state: ISO 3166-2 subdivision code, without country prefix. For example, “NY” for New York, United States. + /// - taxType: The high-level tax type, such as `vat` or `sales_tax`. /// - Returns: A `StripeTaxRate`. func update(taxRate: String, active: Bool?, @@ -58,7 +61,8 @@ public protocol TaxRateRoutes { displayName: String?, jurisdiction: String?, metadata: [String: String]?, - state: String?) -> EventLoopFuture + state: String?, + taxType: StripeTaxRateTaxType?) -> EventLoopFuture /// Returns a list of your tax rates. Tax rates are returned sorted by creation date, with the most recently created tax rates appearing first. /// @@ -79,7 +83,8 @@ extension TaxRateRoutes { description: String? = nil, jurisdiction: String? = nil, metadata: [String: String]? = nil, - state: String? = nil) -> EventLoopFuture { + state: String? = nil, + taxType: StripeTaxRateTaxType? = nil) -> EventLoopFuture { return create(displayName: displayName, inclusive: inclusive, percentage: percentage, @@ -88,7 +93,8 @@ extension TaxRateRoutes { description: description, jurisdiction: jurisdiction, metadata: metadata, - state: state) + state: state, + taxType: taxType) } public func retrieve(taxRate: String) -> EventLoopFuture { @@ -102,7 +108,8 @@ extension TaxRateRoutes { displayName: String? = nil, jurisdiction: String? = nil, metadata: [String: String]? = nil, - state: String? = nil) -> EventLoopFuture { + state: String? = nil, + taxType: StripeTaxRateTaxType? = nil) -> EventLoopFuture { return update(taxRate: taxRate, active: active, country: country, @@ -110,7 +117,8 @@ extension TaxRateRoutes { displayName: displayName, jurisdiction: jurisdiction, metadata: metadata, - state: state) + state: state, + taxType: taxType) } public func listAll(filter: [String: Any]? = nil) -> EventLoopFuture { @@ -136,7 +144,8 @@ public struct StripeTaxRateRoutes: TaxRateRoutes { description: String?, jurisdiction: String?, metadata: [String: String]?, - state: String?) -> EventLoopFuture { + state: String?, + taxType: StripeTaxRateTaxType?) -> EventLoopFuture { var body: [String: Any] = ["display_name": displayName, "inclusive": inclusive, "percentage": percentage] @@ -165,6 +174,10 @@ public struct StripeTaxRateRoutes: TaxRateRoutes { body["state"] = state } + if let taxType = taxType { + body["tax_type"] = taxType.rawValue + } + return apiHandler.send(method: .POST, path: taxrates, body: .string(body.queryParameters), headers: headers) } @@ -179,7 +192,8 @@ public struct StripeTaxRateRoutes: TaxRateRoutes { displayName: String?, jurisdiction: String?, metadata: [String: String]?, - state: String?) -> EventLoopFuture { + state: String?, + taxType: StripeTaxRateTaxType?) -> EventLoopFuture { var body: [String: Any] = [:] if let active = active { @@ -210,6 +224,10 @@ public struct StripeTaxRateRoutes: TaxRateRoutes { body["state"] = state } + if let taxType = taxType { + body["tax_type"] = taxType.rawValue + } + return apiHandler.send(method: .POST, path: "\(taxrates)/\(taxRate)", body: .string(body.queryParameters), headers: headers) } diff --git a/Sources/StripeKit/Checkout/SessionRoutes.swift b/Sources/StripeKit/Checkout/SessionRoutes.swift index 751ecdda..cad56204 100644 --- a/Sources/StripeKit/Checkout/SessionRoutes.swift +++ b/Sources/StripeKit/Checkout/SessionRoutes.swift @@ -18,19 +18,22 @@ public protocol SessionRoutes { /// - allowPromotionCodes: Enables user redeemable promotion codes. /// - billingAddressCollection: Specify whether Checkout should collect the customer’s billing address. If set to `required`, Checkout will always collect the customer’s billing address. If left blank or set to `auto` Checkout will only collect the billing address when necessary. /// - clientReferenceId: A unique string to reference the Checkout Session. This can be a customer ID, a cart ID, or similar, and can be used to reconcile the session with your internal systems. - /// - customer: ID of an existing customer paying for this session, if one exists. May only be used with line_items. Usage with subscription_data is not yet available. If blank, Checkout will create a new customer object based on information provided during the session. The email stored on the customer will be used to prefill the email field on the Checkout page. If the customer changes their email on the Checkout page, the Customer object will be updated with the new email. + /// - customer: ID of an existing customer paying for this session, if one exists. May only be used with `line_items`. Usage with `subscription_data` is not yet available. If blank, Checkout will create a new customer object based on information provided during the session. The email stored on the customer will be used to prefill the email field on the Checkout page. If the customer changes their email on the Checkout page, the Customer object will be updated with the new email. /// - customerEmail: If provided, this value will be used when the Customer object is created. If not provided, customers will be asked to enter their email address. Use this parameter to prefill customer data if you already have an email on file. To access information about the customer once a session is complete, use the customer field. + /// - customerUpdate: Controls what fields on Customer can be updated by the Checkout Session. Can only be provided when customer is provided. /// - discounts: The coupon or promotion code to apply to this Session. Currently, only up to one may be specified. /// - lineItems: A list of items the customer is purchasing. Use this parameter for one-time payments. To create subscriptions, use subscription_data.items. /// - locale: The IETF language tag of the locale Checkout is displayed in. If blank or auto, the browser’s locale is used. Supported values are auto, da, de, en, es, fi, fr, it, ja, nb, nl, pl, pt, sv, or zh. /// - metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to `metadata`. /// - mode: The mode of the Checkout Session, one of `payment`, `setup`, or `subscription`. /// - paymentIntentData: A subset of parameters to be passed to PaymentIntent creation. + /// - paymentMethodOptions: Payment-method-specific configuration. /// - setupIntentData: A subset of parameters to be passed to SetupIntent creation for Checkout Sessions in `setup` mode. /// - shippingAddressCollection: When set, provides configuration for Checkout to collect a shipping address from a customer. /// - shippingRates: The shipping rate to apply to this Session. Currently, only up to one may be specified - /// - submitType: Describes the type of transaction being performed by Checkout in order to customize relevant text on the page, such as the submit button. submit_type can only be specified on Checkout Sessions in payment mode, but not Checkout Sessions in subscription or setup mode. Supported values are `auto`, `book`, `donate`, or `pay`. + /// - submitType: Describes the type of transaction being performed by Checkout in order to customize relevant text on the page, such as the submit button. `submit_type` can only be specified on Checkout Sessions in payment mode, but not Checkout Sessions in subscription or setup mode. Supported values are `auto`, `book`, `donate`, or `pay`. /// - subscriptionData: A subset of parameters to be passed to subscription creation. + /// - taxIdCollection: Controls tax ID collection settings for the session. /// - expand: An array of propertiies to expand. /// - Returns: A `StripeSession`. func create(cancelUrl: String, @@ -41,17 +44,20 @@ public protocol SessionRoutes { clientReferenceId: String?, customer: String?, customerEmail: String?, + customerUpdate: [String: Any]?, discounts: [[String: Any]]?, lineItems: [[String: Any]]?, locale: StripeSessionLocale?, metadata: [String: String]?, mode: StripeSessionMode?, paymentIntentData: [String: Any]?, + paymentMethodOptions: [String: Any]?, setupIntentData: [String: Any]?, shippingAddressCollection: [String: Any]?, shippingRates: [String]?, submitType: StripeSessionSubmitType?, subscriptionData: [String: Any]?, + taxIdCollection: [String: Any]?, expand: [String]?) -> EventLoopFuture /// Retrieves a Session object. @@ -86,17 +92,20 @@ extension SessionRoutes { clientReferenceId: String? = nil, customer: String? = nil, customerEmail: String? = nil, + customerUpdate: [String: Any]? = nil, discounts: [[String: Any]]? = nil, lineItems: [[String: Any]]? = nil, locale: StripeSessionLocale? = nil, metadata: [String: String]? = nil, mode: StripeSessionMode? = nil, paymentIntentData: [String: Any]? = nil, + paymentMethodOptions: [String: Any]? = nil, setupIntentData: [String: Any]? = nil, shippingAddressCollection: [String: Any]? = nil, shippingRates: [String]? = nil, submitType: StripeSessionSubmitType? = nil, subscriptionData: [String: Any]? = nil, + taxIdCollection: [String: Any]? = nil, expand: [String]? = nil) -> EventLoopFuture { return create(cancelUrl: cancelUrl, paymentMethodTypes: paymentMethodTypes, @@ -106,17 +115,20 @@ extension SessionRoutes { clientReferenceId: clientReferenceId, customer: customer, customerEmail: customerEmail, + customerUpdate: customerUpdate, discounts: discounts, lineItems: lineItems, locale: locale, metadata: metadata, mode: mode, paymentIntentData: paymentIntentData, + paymentMethodOptions: paymentMethodOptions, setupIntentData: setupIntentData, shippingAddressCollection: shippingAddressCollection, shippingRates: shippingRates, submitType: submitType, subscriptionData: subscriptionData, + taxIdCollection: taxIdCollection, expand: expand) } @@ -151,17 +163,20 @@ public struct StripeSessionRoutes: SessionRoutes { clientReferenceId: String?, customer: String?, customerEmail: String?, + customerUpdate: [String: Any]?, discounts: [[String: Any]]?, lineItems: [[String: Any]]?, locale: StripeSessionLocale?, metadata: [String: String]?, mode: StripeSessionMode?, paymentIntentData: [String: Any]?, + paymentMethodOptions: [String: Any]?, setupIntentData: [String: Any]?, shippingAddressCollection: [String: Any]?, shippingRates: [String]?, submitType: StripeSessionSubmitType?, subscriptionData: [String: Any]?, + taxIdCollection: [String: Any]?, expand: [String]?) -> EventLoopFuture { var body: [String: Any] = ["cancel_url": cancelUrl, "payment_method_types": paymentMethodTypes.map { $0.rawValue }, @@ -187,6 +202,10 @@ public struct StripeSessionRoutes: SessionRoutes { body["customer_email"] = customerEmail } + if let customerUpdate = customerUpdate { + customerUpdate.forEach { body["customer_update[\($0)]"] = $1 } + } + if let discounts = discounts { body["discounts"] = discounts } @@ -211,6 +230,10 @@ public struct StripeSessionRoutes: SessionRoutes { paymentIntentData.forEach { body["payment_intent_data[\($0)]"] = $1 } } + if let paymentMethodOptions = paymentMethodOptions { + paymentMethodOptions.forEach { body["payment_method_options[\($0)]"] = $1 } + } + if let setupIntentData = setupIntentData { setupIntentData.forEach { body["setup_intent_data[\($0)]"] = $1 } } @@ -231,6 +254,10 @@ public struct StripeSessionRoutes: SessionRoutes { subscriptionData.forEach { body["subscription_data[\($0)]"] = $1 } } + if let taxIdCollection = taxIdCollection { + taxIdCollection.forEach { body["tax_id_collection[\($0)]"] = $1 } + } + if let expand = expand { body["expand"] = expand } diff --git a/Sources/StripeKit/Checkout/Sessions.swift b/Sources/StripeKit/Checkout/Sessions.swift index d744d6a9..66f8c189 100644 --- a/Sources/StripeKit/Checkout/Sessions.swift +++ b/Sources/StripeKit/Checkout/Sessions.swift @@ -27,7 +27,7 @@ public struct StripeSession: StripeModel { public var clientReferenceId: String? /// The ID of the customer for this session. A new customer will be created unless an existing customer was provided in when the session was created. @Expandable public var customer: String? - /// The customer details including the customer’s tax exempt status and the customer’s tax IDs. + /// The customer details including the customer’s tax exempt status and the customer’s tax IDs. Only present on Sessions in `payment` or `subscription` mode. public var customerDetails: StripeSessionCustomerDetails? /// If provided, this value will be used when the Customer object is created. If not provided, customers will be asked to enter their email address. Use this parameter to prefill customer data if you already have an email on file. To access information about the customer once a session is complete, use the `customer` field. public var customerEmail: String? @@ -43,9 +43,11 @@ public struct StripeSession: StripeModel { public var mode: StripeSessionMode? /// The ID of the PaymentIntent created if SKUs or line items were provided. @Expandable public var paymentIntent: String? + /// Payment-method-specific configuration for the PaymentIntent or SetupIntent of this CheckoutSession. + public var paymentMethodOptions: StripeSessionPaymentMethodOptions? /// A list of the types of payment methods (e.g. card) this Checkout Session is allowed to accept. public var paymentMethodTypes: [StripeSessionPaymentMethodType]? - /// The payment status of the Checkout Session, one of paid, unpaid, or no_payment_required. You can use this value to decide when to fulfill your customer’s order. + /// The payment status of the Checkout Session, one of `paid`, `unpaid`, or `no_payment_required`. You can use this value to decide when to fulfill your customer’s order. public var paymentStatus: StripeSessionPaymentStatus? /// The ID of the SetupIntent for Checkout Sessions in setup mode. @Expandable public var setupIntent: String? @@ -59,8 +61,12 @@ public struct StripeSession: StripeModel { @Expandable public var subscription: String? /// The URL the customer will be directed to after the payment or subscription creation is successful. public var successUrl: String? + /// Details on the state of tax ID collection for the session. + public var taxIdCollection: StripeSessionTaxIdCollection? /// Tax and discount details for the computed total amount. public var totalDetails: StripeSessionTotalDetails? + /// The URL to the Checkout Session. + public var url: String? } public struct StripeSessionList: StripeModel { @@ -180,7 +186,77 @@ public enum StripeSessionMode: String, StripeModel { case subscription } +public struct StripeSessionPaymentMethodOptions: StripeModel { + /// If the Checkout Session’s `payment_method_types` includes `acss_debit`, this hash contains the configurations that will be applied to each payment attempt of that type. + public var acssDebit: StripeSessionPaymentMethodOptionsAcssDebit? + /// If the Checkout Session’s `payment_method_types` includes boleto, this hash contains the configurations that will be applied to each payment attempt of that type. + public var boleto: StripeSessionPaymentMethodOptionsBoleto? + /// If the Checkout Session’s `payment_method_types` includes oxxo, this hash contains the configurations that will be applied to each payment attempt of that type. + public var oxxo: StripeSessionPaymentMethodOptionsOXXO? +} + +public struct StripeSessionPaymentMethodOptionsAcssDebit: StripeModel { + /// Currency supported by the bank account. Returned when the Session is in `setup` mode. + public var currency: StripeSessionPaymentMethodOptionsAcssDebitCurrency? + /// Additional fields for Mandate creation + public var mandateOptions: StripeSessionPaymentMethodOptionsAcssDebitMandateOptions? + /// Bank account verification method. + public var verificationMethod: StripeSessionPaymentMethodOptionsAcssDebitVerificationMethod? +} + +public enum StripeSessionPaymentMethodOptionsAcssDebitCurrency: String, StripeModel { + case cad + case usd +} + +public struct StripeSessionPaymentMethodOptionsAcssDebitMandateOptions: StripeModel { + /// A URL for custom mandate text + public var customMandateUrl: String? + /// Description of the interval. Only required if `payment_schedule` parmeter is `interval` or `combined`. + public var intervalDescription: String? + /// Payment schedule for the mandate. + public var paymentSchedule: StripeSessionPaymentMethodOptionsAcssDebitMandateOptionsPaymentSchedule? + /// Transaction type of the mandate. + public var transactionType: StripeSessionPaymentMethodOptionsAcssDebitMandateOptionsTransactionType? +} + +public enum StripeSessionPaymentMethodOptionsAcssDebitMandateOptionsPaymentSchedule: String, StripeModel { + /// Payments are initiated at a regular pre-defined interval + case interval + /// Payments are initiated sporadically + case sporadic + /// Payments can be initiated at a pre-defined interval or sporadically + case combined +} + +public enum StripeSessionPaymentMethodOptionsAcssDebitMandateOptionsTransactionType: String, StripeModel { + /// Transaction are made for personal reasons + case personal + /// Transactions are made for business reasons + case business +} + +public enum StripeSessionPaymentMethodOptionsAcssDebitVerificationMethod: String, StripeModel { + /// Instant verification with fallback to microdeposits. + case automatic + /// Instant verification. + case instant + /// Verification using microdeposits. + case microdeposits +} + +public struct StripeSessionPaymentMethodOptionsBoleto: StripeModel { + /// The number of calendar days before a Boleto voucher expires. For example, if you create a Boleto voucher on Monday and you set `expires_after_days` to 2, the Boleto voucher will expire on Wednesday at 23:59 America/Sao_Paulo time. + public var expiresAfterDays: Int? +} + +public struct StripeSessionPaymentMethodOptionsOXXO: StripeModel { + /// The number of calendar days before an OXXO invoice expires. For example, if you create an OXXO invoice on Monday and you set `expires_after_days` to 2, the OXXO invoice will expire on Wednesday at 23:59 America/Mexico_City time. + public var expiresAfterDays: Int? +} + public enum StripeSessionPaymentMethodType: String, StripeModel { + case alipay case card case ideal case fpx @@ -189,6 +265,14 @@ public enum StripeSessionPaymentMethodType: String, StripeModel { case giropay case p24 case eps + case sofort + case sepaDebit = "sepa_debit" + case grabpay + case afterpayClearpay = "afterpay_clearpay" + case acssDebit = "acss_debit" + case wechatPay = "wechat_pay" + case boleto + case oxxo } public struct StripeSessionShippingAddressCollection: StripeModel { @@ -229,3 +313,8 @@ public enum StripeSessionPaymentStatus: String, StripeModel { /// The Checkout Session is in setup mode and doesn’t require a payment at this time. case noPaymentRequired = "no_payment_required" } + +public struct StripeSessionTaxIdCollection: StripeModel { + /// Indicates whether tax ID collection is enabled for the session + public var enabled: Bool? +} diff --git a/Sources/StripeKit/Connect/Accounts/Account.swift b/Sources/StripeKit/Connect/Accounts/Account.swift index e590e261..ce04f68e 100644 --- a/Sources/StripeKit/Connect/Accounts/Account.swift +++ b/Sources/StripeKit/Connect/Accounts/Account.swift @@ -8,7 +8,7 @@ import Foundation -/// The [Account Object](https://stripe.com/docs/api/accounts/object). +/// The [Account Object](https://stripe.com/docs/api/accounts/object) public struct StripeConnectAccount: StripeModel { /// Unique identifier for the object. public var id: String @@ -22,6 +22,8 @@ public struct StripeConnectAccount: StripeModel { public var capabilities: StripeConnectAccountCapablities? /// Whether the account can create live charges. public var chargesEnabled: Bool? + /// The controller of the account. This field is only available for Standard accounts. + public var controller: StripeConnectAccountController? /// Information about the company or business. This field is null unless business_type is set to company. public var company: StripeConnectAccountCompany? /// The account’s country. @@ -86,6 +88,8 @@ public enum StripeConnectAccountBusinessType: String, StripeModel { } public struct StripeConnectAccountCapablities: StripeModel { + /// The status of the ACSS Direct Debits payments capability of the account, or whether the account can directly process ACSS Direct Debits charges. + public var acssDebitPayments: StripeConnectAccountCapabilitiesStatus? /// The status of the Afterpay Clearpay capability of the account, or whether the account can directly process Afterpay Clearpay charges. public var afterpayClearpayPayments: StripeConnectAccountCapabilitiesStatus? /// The status of the BECS Direct Debit (AU) payments capability of the account, or whether the account can directly process BECS Direct Debit (AU) charges. @@ -136,6 +140,18 @@ public enum StripeConnectAccountCapabilitiesStatus: String, StripeModel { case pending } +public struct StripeConnectAccountController: StripeModel { + /// `true` if the Connect application retrieving the resource controls the account and can therefore exercise platform controls. Otherwise, this field is null. + public var isController: Bool? + /// The controller type. Can be `application`, if a Connect application controls the account, or `account`, if the account controls itself. + public var type: StripeConnectAccountControllerType? +} + +public enum StripeConnectAccountControllerType: String, StripeModel { + case application + case account +} + public struct StripeConnectAccountCompany: StripeModel { /// The company’s primary address. public var address: StripeAddress? @@ -222,19 +238,19 @@ public enum StripeConnectAccountCompanyStructure: String, StripeModel { } public struct StripeConnectAccountRequirements: StripeModel { - /// The date the fields in `currently_due` must be collected by to keep payouts enabled for the account. These fields might block payouts sooner if the next threshold is reached before these fields are collected. + /// Date by which the fields in `currently_due` must be collected to keep the account enabled. These fields may disable the account sooner if the next threshold is reached before they are collected. public var currentDeadline: Date? - /// The fields that need to be collected to keep the account enabled. If not collected by the `current_deadline`, these fields appear in `past_due` as well, and the account is disabled. + /// Fields that need to be collected to keep the account enabled. If not collected by `current_deadline`, these fields appear in `past_due` as well, and the account is disabled. public var currentlyDue: [String]? - /// If the account is disabled, this string describes why the account can’t create charges or receive payouts. Can be `requirements.past_due`, `requirements.pending_verification`, `rejected.fraud`, `rejected.terms_of_service`, `rejected.listed`, `rejected.other`, `listed`, `under_review`, or `other`. + /// If the account is disabled, this string describes why. Can be `requirements.past_due`, `requirements.pending_verification`, `listed`, `platform_paused`, `rejected.fraud`, `rejected.listed`, `rejected.terms_of_service`, `rejected.other`, `under_review`, or `other`. public var disabledReason: StripeConnectAccountRequirementsDisabledReason? - /// The fields that are `currently_due` and need to be collected again because validation or verification failed for some reason. + /// Fields that are `currently_due` and need to be collected again because validation or verification failed. public var errors: [StripeConnectAccountRequirementsError]? - /// The fields that need to be collected assuming all volume thresholds are reached. As they become required, these fields appear in `currently_due` as well, and the `current_deadline` is set. + /// Fields that need to be collected assuming all volume thresholds are reached. As they become required, they appear in `currently_due` as well, and `current_deadline` becomes set. public var eventuallyDue: [String]? - /// The fields that weren’t collected by the current_deadline. These fields need to be collected to re-enable the account. + /// Fields that weren’t collected by `current_deadline`. These fields need to be collected to enable the account. public var pastDue: [String]? - /// Fields that may become required depending on the results of verification or review. An empty array unless an asynchronous verification is pending. If verification fails, the fields in this array become required and move to `currently_due` or `past_due`. + /// Fields that may become required depending on the results of verification or review. Will be an empty array unless an asynchronous verification is pending. If verification fails, these fields move to `eventually_due`, `currently_due`, or `past_due`. public var pendingVerification: [String]? } @@ -340,6 +356,12 @@ public enum StripeConnectAccountRequirementsErrorCode: String, StripeModel { case verificationFailedTaxIdNotIssued = "verification_failed_tax_id_not_issued" /// Verification failed for an unknown reason. Correct any errors and resubmit the required fields. case verificationFailedOther = "verification_failed_other" + /// We have identified owners that haven’t been added on the account. Add any missing owners to the account. + case verificationMissingOwners = "verification_missing_owners" + /// We have identified executives that haven’t been added on the account. Add any missing executives to the account. + case verificationMissingExecutives = "verification_missing_executives" + /// We have identified holding companies with significant percentage ownership. Upload a Memorandum of Association for each of the holding companies. + case verificationRequiresAdditionalMemorandumOfAssociations = "verification_requires_additional_memorandum_of_associations" } public struct StripeConnectAccountSettings: StripeModel { diff --git a/Sources/StripeKit/Connect/Capabilities/Capabilities.swift b/Sources/StripeKit/Connect/Capabilities/Capabilities.swift index 2703bffa..7c8e7d69 100644 --- a/Sources/StripeKit/Connect/Capabilities/Capabilities.swift +++ b/Sources/StripeKit/Connect/Capabilities/Capabilities.swift @@ -16,6 +16,8 @@ public struct StripeCapability: StripeModel { @Expandable public var account: String? /// Whether the capability has been requested. public var requested: Bool? + /// + public var requirements: StripeCapabilitiesRequirements? /// Time at which the capability was requested. Measured in seconds since the Unix epoch. public var requestedAt: Date? /// The status of the capability. Can be active, inactive, pending, or unrequested. @@ -23,17 +25,24 @@ public struct StripeCapability: StripeModel { } public struct StripeCapabilitiesRequirements: StripeModel { - /// The date the fields in `currently_due` must be collected by to keep the capability enabled for the account. + /// Date by which the fields in `currently_due` must be collected to keep the capability enabled for the account. These fields may disable the capability sooner if the next threshold is reached before they are collected. public var currentDeadline: Date? - /// The fields that need to be collected to keep the capability enabled. If not collected by the `current_deadline`, these fields appear in `past_due` as well, and the capability is disabled. + /// Fields that need to be collected to keep the capability enabled. If not collected by `current_deadline`, these fields appear in `past_due` as well, and the capability is disabled. public var currentlyDue: [String]? - /// If the capability is disabled, this string describes why. Possible values are `requirement.fields_needed`, `pending.onboarding`, `pending.review`, `rejected_fraud`, or `rejected.other`. + /// If the capability is disabled, this string describes why. Can be `requirements.past_due`, `requirements.pending_verification`, `listed`, `platform_paused`, `rejected.fraud`, `rejected.listed`, `rejected.terms_of_service`, `rejected.other`, `under_review`, or `other`. + /// + /// `rejected.unsupported_business` means that the account’s business is not supported by the capability. For example, payment methods may restrict the businesses they support in their terms of service: + /// - [Adterpay Clearpay's terms of service](https://stripe.com/afterpay-clearpay/legal#restricted-businesses) + /// + /// If you believe that the rejection is in error, please contact support@stripe.com for assistance. public var disabledReason: String? - /// The fields that need to be collected assuming all volume thresholds are reached. As they become required, these fields appear in `currently_due` as well, and the `current_deadline` is set. + /// Fields that are `currently_due` and need to be collected again because validation or verification failed. + public var errors: [StripeConnectAccountRequirementsError]? + /// Fields that need to be collected assuming all volume thresholds are reached. As they become required, they appear in `currently_due` as well, and `current_deadline` becomes set. public var eventuallyDue: [String]? - /// The fields that weren’t collected by the `current_deadline`. These fields need to be collected to enable the capability for the account. + /// Fields that weren’t collected by `current_deadline`. These fields need to be collected to enable the capability on the account. public var pastDue: [String]? - /// Fields that may become required depending on the results of verification or review. An empty array unless an asynchronous verification is pending. If verification fails, the fields in this array become required and move to `currently_due` or `past_due`. + /// Fields that may become required depending on the results of verification or review. Will be an empty array unless an asynchronous verification is pending. If verification fails, these fields move to `eventually_due`, `currently_due`, or `past_due`. public var pendingVerification: [String]? } diff --git a/Sources/StripeKit/Connect/Persons/Person.swift b/Sources/StripeKit/Connect/Persons/Person.swift index 36f3d8ea..cb90f8db 100644 --- a/Sources/StripeKit/Connect/Persons/Person.swift +++ b/Sources/StripeKit/Connect/Persons/Person.swift @@ -35,7 +35,7 @@ public struct StripePerson: StripeModel { public var firstNameKanji: String? /// The person’s gender (International regulations require either “male” or “female”). public var gender: StripePersonGender? - /// Whether the person’s id_number was provided. + /// Whether the person’s `id_number` was provided. public var idNumberProvided: Bool? /// The person's last name. public var lastName: String? @@ -102,7 +102,7 @@ public struct StripePersonRelationship: StripeModel { public struct StripePersonRequirements: StripeModel { /// Fields that need to be collected to keep the person’s account enabled. If not collected by the account’s `current_deadline`, these fields appear in `past_due` as well, and the account is disabled. public var currentlyDue: [String]? - /// The fields that are currently_due and need to be collected again because validation or verification failed for some reason. + /// The fields that are `currently_due` and need to be collected again because validation or verification failed for some reason. public var errors: [StripePersonRequirementsError]? /// Fields that need to be collected assuming all volume thresholds are reached. As fields are needed, they are moved to `currently_due` and the account’s `current_deadline` is set. public var eventuallyDue: [String]? diff --git a/Sources/StripeKit/Connect/Persons/PersonRoutes.swift b/Sources/StripeKit/Connect/Persons/PersonRoutes.swift index 74989a53..4543443a 100644 --- a/Sources/StripeKit/Connect/Persons/PersonRoutes.swift +++ b/Sources/StripeKit/Connect/Persons/PersonRoutes.swift @@ -16,6 +16,7 @@ public protocol PersonRoutes { /// - address: The person’s address. /// - addressKana: The Kana variation of the person’s address (Japan only). /// - addressKanji: The Kanji variation of the person’s address (Japan only). + /// - documents: Documents that may be submitted to satisfy various informational requests. /// - dob: The person’s date of birth. /// - email: The person’s email address. /// - firstName: The person’s first name. @@ -40,6 +41,7 @@ public protocol PersonRoutes { address: [String: Any]?, addressKana: [String: Any]?, addressKanji: [String: Any]?, + documents: [String: Any]?, dob: [String: Any]?, email: String?, firstName: String?, @@ -76,6 +78,7 @@ public protocol PersonRoutes { /// - address: The person’s address. /// - addressKana: The Kana variation of the person’s address (Japan only). /// - addressKanji: The Kanji variation of the person’s address (Japan only). + /// - documents: Documents that may be submitted to satisfy various informational requests. /// - dob: The person’s date of birth. /// - email: The person’s email address. /// - firstName: The person’s first name. @@ -101,6 +104,7 @@ public protocol PersonRoutes { address: [String: Any]?, addressKana: [String: Any]?, addressKanji: [String: Any]?, + documents: [String: Any]?, dob: [String: Any]?, email: String?, firstName: String?, @@ -146,6 +150,7 @@ extension PersonRoutes { address: [String: Any]? = nil, addressKana: [String: Any]? = nil, addressKanji: [String: Any]? = nil, + documents: [String: Any]? = nil, dob: [String: Any]? = nil, email: String? = nil, firstName: String? = nil, @@ -169,6 +174,7 @@ extension PersonRoutes { address: address, addressKana: addressKana, addressKanji: addressKanji, + documents: documents, dob: dob, email: email, firstName: firstName, @@ -199,6 +205,7 @@ extension PersonRoutes { address: [String: Any]? = nil, addressKana: [String: Any]? = nil, addressKanji: [String: Any]? = nil, + documents: [String: Any]? = nil, dob: [String: Any]? = nil, email: String? = nil, firstName: String? = nil, @@ -223,6 +230,7 @@ extension PersonRoutes { address: address, addressKana: addressKana, addressKanji: addressKanji, + documents: documents, dob: dob, email: email, firstName: firstName, @@ -268,6 +276,7 @@ public struct StripePersonRoutes: PersonRoutes { address: [String: Any]?, addressKana: [String: Any]?, addressKanji: [String: Any]?, + documents: [String: Any]?, dob: [String: Any]?, email: String?, firstName: String?, @@ -301,6 +310,10 @@ public struct StripePersonRoutes: PersonRoutes { addressKanji.forEach { body["address_kanji[\($0)]"] = $1 } } + if let documents = documents { + documents.forEach { body["documents[\($0)]"] = $1 } + } + if let dob = dob { dob.forEach { body["dob[\($0)]"] = $1 } } @@ -389,6 +402,7 @@ public struct StripePersonRoutes: PersonRoutes { address: [String: Any]?, addressKana: [String: Any]?, addressKanji: [String: Any]?, + documents: [String: Any]?, dob: [String: Any]?, email: String?, firstName: String?, @@ -422,6 +436,10 @@ public struct StripePersonRoutes: PersonRoutes { addressKanji.forEach { body["address_kanji[\($0)]"] = $1 } } + if let documents = documents { + documents.forEach { body["documents[\($0)]"] = $1 } + } + if let dob = dob { dob.forEach { body["dob[\($0)]"] = $1 } } diff --git a/Sources/StripeKit/Core Resources/Events/Event.swift b/Sources/StripeKit/Core Resources/Events/Event.swift index baecccda..bc918588 100644 --- a/Sources/StripeKit/Core Resources/Events/Event.swift +++ b/Sources/StripeKit/Core Resources/Events/Event.swift @@ -74,6 +74,7 @@ public enum StripeEventObject: StripeModel { case product(StripeProduct) case promotionCode(StripePromotionCode) case earlyFraudWarniing(StripeEarlyFraudWarning) + case quote(StripeQuote) case reportRun(StripeReportRun) case reportType(StripeReportType) case review(StripeReview) @@ -84,6 +85,7 @@ public enum StripeEventObject: StripeModel { case taxRate(StripeTaxRate) case topup(StripeTopUp) case transfer(StripeTransfer) + case verificationSession(StripeVerificationSession) public init(from decoder: Decoder) throws { let object = try decoder @@ -128,6 +130,8 @@ public enum StripeEventObject: StripeModel { self = try .taxId(StripeTaxID(from: decoder)) case "file": self = try .file(StripeFile(from: decoder)) + case "identity.verification_session": + self = try .verificationSession(StripeVerificationSession(from: decoder)) case "invoice": self = try .invoice(StripeInvoice(from: decoder)) case "invoiceitem": @@ -166,6 +170,8 @@ public enum StripeEventObject: StripeModel { self = try .promotionCode(StripePromotionCode(from: decoder)) case "radar.early_fraud_warning": self = try .earlyFraudWarniing(StripeEarlyFraudWarning(from: decoder)) + case "quote": + self = try .quote(StripeQuote(from: decoder)) case "reporting.report_run": self = try .reportRun(StripeReportRun(from: decoder)) case "reporting.report_type": @@ -318,6 +324,18 @@ public enum StripeEventType: String, StripeModel { case customerTaxIdUpdated = "customer.tax_id.updated" /// Occurs whenever a new Stripe-generated file is available for your account. case fileCreated = "file.created" + /// Occurs whenever a VerificationSession is canceled + case identityVerificationSessionCanceled = "identity.verification_session.canceled" + /// Occurs whenever a VerificationSession is created + case identityVerificationSessionCreated = "identity.verification_session.created" + /// Occurs whenever a VerificationSession transitions to processing + case identityVerificationSessionProcessing = "identity.verification_session.processing" + /// Occurs whenever a VerificationSession is redacted. You must create a webhook endpoint which explicitly subscribes to this event type to access it. Webhook endpoints which subscribe to all events will not include this event type. + case identityVerificationSessionRedacted = "identity.verification_session.redacted" + /// Occurs whenever a VerificationSession transitions to require user input + case identityVerificationSessionRequiresInput = "identity.verification_session.requires_input" + /// Occurs whenever a VerificationSession transitions to verified + case identityVerificationSessionVerified = "identity.verification_session.verified" /// Occurs whenever a new invoice is created. To learn how webhooks can be used with this event, and how they can affect it, see Using Webhooks with Subscriptions. case invoiceCreated = "invoice.created" /// Occurs whenever a draft invoice is deleted. @@ -450,6 +468,14 @@ public enum StripeEventType: String, StripeModel { case promotionCodeCreated = "promotion_code.created" /// Occurs whenever a promotion code is updated. case promotionCodeUpdated = "promotion_code.updated" + /// Occurs whenever a quote is accepted. + case quoteAccepted = "quote.accepted" + /// Occurs whenever a quote is canceled. + case quoteCanceled = "quote.canceled" + /// Occurs whenever a quote is created. + case quoteCreated = "quote.created" + /// Occurs whenever a quote is finalized. + case quoteFinalized = "quote.finalized" /// Occurs whenever an early fraud warning is created. case radarEarlyFraudWarningCreated = "radar.early_fraud_warning.created" /// Occurs whenever an early fraud warning is updated. diff --git a/Sources/StripeKit/Core Resources/Files/File.swift b/Sources/StripeKit/Core Resources/Files/File.swift index c518f12b..89ef7580 100644 --- a/Sources/StripeKit/Core Resources/Files/File.swift +++ b/Sources/StripeKit/Core Resources/Files/File.swift @@ -21,7 +21,7 @@ public struct StripeFile: StripeModel { public var filename: String? /// A list of file links. public var links: StripeFileLinkList? - /// The purpose of the file. Possible values are `business_icon`, `business_logo`, `customer_signature`, `dispute_evidence`, `finance_report_run`, `identity_document`, `pci_document`, `sigma_scheduled_query`, or `tax_document_user_upload`. + /// The [purpose](https://stripe.com/docs/file-upload#uploading-a-file) of the uploaded file. public var purpose: StripeFilePurpose? /// The size in bytes of the file upload object. public var size: Int? diff --git a/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntent.swift b/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntent.swift index a4420b20..bfc3ab2f 100644 --- a/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntent.swift +++ b/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntent.swift @@ -7,7 +7,7 @@ import Foundation -/// The [PaymentIntent Object](https://stripe.com/docs/api/payment_intents/object). +/// The [PaymentIntent Object](https://stripe.com/docs/api/payment_intents/object) public struct StripePaymentIntent: StripeModel { /// Unique identifier for the object. public var id: String @@ -65,7 +65,7 @@ public struct StripePaymentIntent: StripeModel { public var receiptEmail: String? /// ID of the review associated with this PaymentIntent, if any. @Expandable public var review: String? - /// Indicates that you intend to make future payments with this PaymentIntent’s payment method. If present, the payment method used with this PaymentIntent can be attached to a Customer, even after the transaction completes. Use `on_session` if you intend to only reuse the payment method when your customer is present in your checkout flow. Use `off_session` if your customer may or may not be in your checkout flow. For more, learn to save card details after a payment. Stripe uses `setup_future_usage` to dynamically optimize your payment flow and comply with regional legislation and network rules. For example, if your customer is impacted by SCA, using off_session will ensure that they are authenticated while processing this PaymentIntent. You will then be able to collect off-session payments for this customer. + /// Indicates that you intend to make future payments with this PaymentIntent’s payment method. If present, the payment method used with this PaymentIntent can be attached to a Customer, even after the transaction completes. Use `on_session` if you intend to only reuse the payment method when your customer is present in your checkout flow. Use `off_session` if your customer may or may not be in your checkout flow. For more, learn to save card details after a payment. Stripe uses `setup_future_usage` to dynamically optimize your payment flow and comply with regional legislation and network rules. For example, if your customer is impacted by SCA, using `off_session` will ensure that they are authenticated while processing this PaymentIntent. You will then be able to collect off-session payments for this customer. public var setupFutureUsage: StripePaymentIntentSetupFutureUsage? /// Shipping information for this PaymentIntent. public var shipping: StripeShippingLabel? @@ -117,10 +117,55 @@ public enum StripePaymentIntentConfirmationMethod: String, StripeModel { } public struct StripePaymentIntentNextAction: StripeModel { + /// Contains instructions for authenticating a payment by redirecting your customer to Alipay App or website. + public var alipayHandleRedirect: StripePaymentIntentNextActionAlipayHandleRedirect? + /// Contains Boleto details necessary for the customer to complete the payment. + public var boletoDisplaydetails: StripePaymentIntentNextActionBoletoDisplayDetails? + /// Contains OXXO details necessary for the customer to complete the payment. + public var oxxoDisplayDetails: StripePaymentIntentNextActionOXXODisplayDetails? /// Contains instructions for authenticating a payment by redirecting your customer to another page or application. public var redirectToUrl: StripePaymentIntentNextActionRedirectToUrl? - /// Type of the next action to perform, one of `redirect_to_url` or `use_stripe_sdk`. + /// Type of the next action to perform, one of `redirect_to_url` or `use_stripe_sdk`, `alipay_handle_redirect`, or `oxxo_display_details`. public var type: StripePaymentIntentNextActionType? + /// Contains details describing microdeposits verification flow. + public var verifyWithMicrodeposits: StripePaymentIntentNextActionVerifyWithMicrodeposits? + /// The field that contains Wechat Pay QR code info + public var wechatPayDisplayQrCode: StripePaymentIntentNextActionWechatPayQRCode? + /// Info required for android app to app redirect + public var wechatPayRedirectToAndroidApp: StripePaymentIntentNextActionWechatPayAndroidApp? + /// Info required for iOS app to app redirect + public var wechatPayRedirectToIosApp: StripePaymentIntentNextActionWechatPayIOSApp? +} + +public struct StripePaymentIntentNextActionAlipayHandleRedirect: StripeModel { + /// The native data to be used with Alipay SDK you must redirect your customer to in order to authenticate the payment in an Android App. + public var nativeData: String? + /// The native URL you must redirect your customer to in order to authenticate the payment in an iOS App. + public var nativeUrl: String? + /// If the customer does not exit their browser while authenticating, they will be redirected to this specified URL after completion. + public var returnUrl: String? + /// The URL you must redirect your customer to in order to authenticate the payment. + public var url: String? +} + +public struct StripePaymentIntentNextActionBoletoDisplayDetails: StripeModel { + /// The timestamp after which the boleto expires. + public var expiresAt: Date? + /// The URL to the hosted boleto voucher page, which allows customers to view the boleto voucher. + public var hostedVoucherUrl: String? + /// The boleto number. + public var number: String? + /// The URL to the downloadable boleto voucher PDF. + public var pdf: String? +} + +public struct StripePaymentIntentNextActionOXXODisplayDetails: StripeModel { + /// The timestamp after which the OXXO voucher expires. + public var expiresAfter: Date? + /// The URL for the hosted OXXO voucher page, which allows customers to view and print an OXXO voucher. + public var hostedVoucherUrl: String? + /// OXXO reference number. + public var number: String? } public struct StripePaymentIntentNextActionRedirectToUrl: StripeModel { @@ -128,16 +173,49 @@ public struct StripePaymentIntentNextActionRedirectToUrl: StripeModel { public var returnUrl: String? /// The URL you must redirect your customer to in order to authenticate the payment. public var url: String? - /** - https://stripe.com/docs/api/payment_intents/object#payment_intent_object-next_action-use_stripe_sdk - Stripe .net doesn't implement the `use_stripe_sdk` property (probably due to its dynamic nature) so neither am I :) - https://github.com/stripe/stripe-dotnet/blob/master/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextAction.cs - */ } public enum StripePaymentIntentNextActionType: String, StripeModel { case redirectToUrl = "redirect_to_url" case useStripeSDK = "use_stripe_sdk" + case alipayHandleRedirect = "alipay_handle_redirect" + case oxxoDisplayDetails = "oxxo_display_details" +} + +public struct StripePaymentIntentNextActionVerifyWithMicrodeposits: StripeModel { + /// The timestamp when the microdeposits are expected to land. + public var arrivalDate: Date? + /// The URL for the hosted verification page, which allows customers to verify their bank account. + public var hostedVerificationUrl: String? +} + +public struct StripePaymentIntentNextActionWechatPayQRCode: StripeModel { + /// The data being used to generate QR code + public var data: String? + /// The base64 image data for a pre-generated QR code + public var imageDataUrl: String? +} + +public struct StripePaymentIntentNextActionWechatPayAndroidApp: StripeModel { + /// `app_id` is the APP ID registered on WeChat open platform + public var appId: String? + /// `nonce_str` is a random string + public var nonceStr: String? + /// Package is static value + public var package: String? + /// A unique merchant ID assigned by Wechat Pay + public var partnerId: String? + /// A unique trading ID assigned by Wechat Pay + public var prepayId: String? + /// A signature + public var sign: String? + /// Specifies the current time in epoch format + public var timestamp: String? +} + +public struct StripePaymentIntentNextActionWechatPayIOSApp: StripeModel { + /// An universal link that redirect to Wechat Pay APP + public var nativeUrl: String? } public enum StripePaymentIntentStatus: String, StripeModel { @@ -158,12 +236,22 @@ public struct StripePaymentIntentsList: StripeModel { } public struct StripePaymentIntentPaymentMethodOptions: StripeModel { + /// If the PaymentIntent’s `payment_method_types` includes `acss_debit`, this hash contains the configurations that will be applied to each payment attempt of that type. + public var acssDebit: StripePaymentIntentPaymentMethodOptionsAcssDebit? + /// If the PaymentIntent’s `payment_method_types` includes `afterpay_clearpay`, this hash contains the configurations that will be applied to each payment attempt of that type. + public var afterpayClearpay: StripePaymentIntentPaymentMethodOptionsAfterpayClearpay? /// If the PaymentIntent’s `payment_method_types` includes `alipay`, this hash contains the configurations that will be applied to each payment attempt of that type. public var alipay: StripePaymentIntentPaymentMethodOptionsAlipay? /// If the PaymentIntent’s `payment_method_types` includes `bancontact`, this hash contains the configurations that will be applied to each payment attempt of that type. public var bancontact: StripePaymentIntentPaymentMethodOptionsBancontact? + /// If the PaymentIntent’s `payment_method_types` includes `boleto`, this hash contains the configurations that will be applied to each payment attempt of that type. + public var boleto: StripePaymentIntentPaymentMethodOptionsBoleto? /// If the PaymentIntent’s `payment_method_types` includes `card`, this hash contains the configurations that will be applied to each payment attempt of that type. public var card: StripePaymentIntentPaymentMethodOptionsCard? + /// If the PaymentIntent’s `payment_method_types` includes `card_present`, this hash contains the configurations that will be applied to each payment attempt of that type. + public var cardPresent: StripePaymentIntentPaymentMethodOptionsCardPresent? + /// If the PaymentIntent’s `payment_method_types` includes `ideal`, this hash contains the configurations that will be applied to each payment attempt of that type. + public var ideal: StripePaymentIntentPaymentMethodOptionsIdeal? /// If the PaymentIntent’s `payment_method_types` includes `oxxo`, this hash contains the configurations that will be applied to each payment attempt of that type. public var oxxo: StripePaymentIntentPaymentMethodOptionsOXXO? /// If the PaymentIntent’s `payment_method_types` includes `p24`, this hash contains the configurations that will be applied to each payment attempt of that type. @@ -172,10 +260,60 @@ public struct StripePaymentIntentPaymentMethodOptions: StripeModel { public var sepaDebit: StripePaymentIntentPaymentMethodOptionsSepaDebit? /// If the PaymentIntent’s `payment_method_types` includes `sofort`, this hash contains the configurations that will be applied to each payment attempt of that type. public var sofort: StripePaymentIntentPaymentMethodOptionsSofort? + /// If the PaymentIntent’s `payment_method_types` includes `wechat_pay`, this hash contains the configurations that will be applied to each payment attempt of that type. + public var wechatPay: StripePaymentIntentPaymentMethodOptionsWechatPay? +} + +public struct StripePaymentIntentPaymentMethodOptionsAcssDebit: StripeModel { + /// Additional fields for Mandate creation + public var mandateOptions: StripePaymentIntentPaymentMethodOptionsAcssDebitMandateOptions? + /// Bank account verification method. + public var verificationMethod: StripePaymentIntentPaymentMethodOptionsAcssDebitVerificationMethod? } -public struct StripePaymentIntentPaymentMethodOptionsAlipay: StripeModel { - +public struct StripePaymentIntentPaymentMethodOptionsAcssDebitMandateOptions: StripeModel { + /// A URL for custom mandate text + public var customMandateUrl: String? + /// Description of the interval. Only required if `payment_schedule` parmeter is `interval` or `combined`. + public var intervalDescription: String? + /// Payment schedule for the mandate. + public var paymentSchedule: StripePaymentIntentPaymentMethodOptionsAcssDebitMandateOptionsPaymentSchedule? + /// Transaction type of the mandate. + public var transactionType: StripePaymentIntentPaymentMethodOptionsAcssDebitMandateOptionsTransactionType? +} + +public enum StripePaymentIntentPaymentMethodOptionsAcssDebitMandateOptionsPaymentSchedule: String, StripeModel { + /// Payments are initiated at a regular pre-defined interval + case interval + /// Payments are initiated sporadically + case sporadic + /// Payments can be initiated at a pre-defined interval or sporadically + case combined +} + +public enum StripePaymentIntentPaymentMethodOptionsAcssDebitMandateOptionsTransactionType: String, StripeModel { + /// Payments are initiated at a regular pre-defined interval + case interval + /// Payments are initiated sporadically + case sporadic + /// Payments can be initiated at a pre-defined interval or sporadically + case combined +} + +public enum StripePaymentIntentPaymentMethodOptionsAcssDebitVerificationMethod: String, StripeModel { + /// Instant verification with fallback to microdeposits. + case automatic + /// Instant verification. + case instant + /// Verification using microdeposits. + case microdeposits +} + +public struct StripePaymentIntentPaymentMethodOptionsAlipay: StripeModel {} + +public struct StripePaymentIntentPaymentMethodOptionsAfterpayClearpay: StripeModel { + /// Order identifier shown to the merchant in Afterpay’s online portal. We recommend using a value that helps you answer any questions a customer might have about the payment. The identifier is limited to 128 characters and may contain only letters, digits, underscores, backslashes and dashes. + public var reference: String? } public struct StripePaymentIntentPaymentMethodOptionsBancontact: StripeModel { @@ -183,6 +321,11 @@ public struct StripePaymentIntentPaymentMethodOptionsBancontact: StripeModel { public var preferredLanguage: String? } +public struct StripePaymentIntentPaymentMethodOptionsBoleto: StripeModel { + /// The number of calendar days before a Boleto voucher expires. For example, if you create a Boleto voucher on Monday and you set `expires_after_days` to 2, the Boleto voucher will expire on Wednesday at 23:59 America/Sao_Paulo time. + public var expiresAfterDays: Int? +} + public struct StripePaymentIntentPaymentMethodOptionsCard: StripeModel { /// Installment details for this payment (Mexico only). For more information, see the installments integration guide. public var installments: StripePaymentIntentPaymentMethodOptionsCardInstallments? @@ -202,23 +345,41 @@ public struct StripePaymentIntentPaymentMethodOptionsCardInstallments: StripeMod public var plan: StripeChargePaymentDetailsCardInstallmentPlan? } +public struct StripePaymentIntentPaymentMethodOptionsCardPresent: StripeModel {} + +public struct StripePaymentIntentPaymentMethodOptionsIdeal: StripeModel {} + public struct StripePaymentIntentPaymentMethodOptionsOXXO: StripeModel { /// The number of calendar days before an OXXO invoice expires. For example, if you create an OXXO invoice on Monday and you set `expires_after_days` to 2, the OXXO invoice will expire on Wednesday at 23:59 America/Mexico_City time. public var expiresAfterDays: Int? } -public struct StripePaymentIntentPaymentMethodOptionsP24: StripeModel { -} +public struct StripePaymentIntentPaymentMethodOptionsP24: StripeModel {} public struct StripePaymentIntentPaymentMethodOptionsSepaDebit: StripeModel { /// Additional fields for Mandate creation public var mandateOptions: StripePaymentIntentPaymentMethodOptionsSepaDebitMandateOptions? } -public struct StripePaymentIntentPaymentMethodOptionsSepaDebitMandateOptions: StripeModel { -} +public struct StripePaymentIntentPaymentMethodOptionsSepaDebitMandateOptions: StripeModel {} public struct StripePaymentIntentPaymentMethodOptionsSofort: StripeModel { /// Preferred language of the SOFORT authorization page that the customer is redirected to. public var preferredLanguage: String? } + +public struct StripePaymentIntentPaymentMethodOptionsWechatPay: StripeModel { + /// The app ID registered with WeChat Pay. Only required when client is ios or android. + public var appId: String? + /// The client type that the end customer will pay from + public var client: StripePaymentIntentPaymentMethodOptionsWechatPayClient? +} + +public enum StripePaymentIntentPaymentMethodOptionsWechatPayClient: String, StripeModel { + /// The end customer will pay from web browser + case web + /// The end customer will pay from an iOS app + case ios + /// The end customer will pay from an Android app + case android +} diff --git a/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntentsRoutes.swift b/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntentsRoutes.swift index 6f26be9f..45f1be20 100644 --- a/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntentsRoutes.swift +++ b/Sources/StripeKit/Core Resources/PaymentIntents/PaymentIntentsRoutes.swift @@ -29,10 +29,10 @@ public protocol PaymentIntentsRoutes { /// - paymentMethod: ID of the payment method to attach to this PaymentIntent. /// - paymentMethodData: If provided, this hash will be used to create a PaymentMethod. The new PaymentMethod will appear in the `payment_method` property on the PaymentIntent. /// - paymentMethodOptions: Payment-method-specific configuration for this PaymentIntent. - /// - paymentMethodTypes: The list of payment method types that this PaymentIntent is allowed to use. If this is not provided, defaults to `[“card”]`. Valid payment method types include: `alipay` `au_becs_debit` `bancontact` `card` `card_present` `eps` `giropay` `ideal` `interact_present` `p24` `sepa_debit` and `sofort`. + /// - paymentMethodTypes: The list of payment method types that this PaymentIntent is allowed to use. If this is not provided, defaults to `[“card”]`. Valid payment method types include: `acss_debit` `alipay` `au_becs_debit` `bancontact` `card` `card_present` `eps` `giropay` `ideal` `p24` `sepa_debit` and `sofort`. /// - receiptEmail: Email address that the receipt for the resulting payment will be sent to. /// - returnUrl: The URL to redirect your customer back to after they authenticate or cancel their payment on the payment method’s app or site. If you’d prefer to redirect to a mobile application, you can alternatively supply an application URI scheme. This parameter can only be used with confirm=true. - /// - setupFutureUsage: Indicates that you intend to make future payments with this PaymentIntent’s payment method. If present, the payment method used with this PaymentIntent can be attached to a Customer, even after the transaction completes. Use `on_session` if you intend to only reuse the payment method when your customer is present in your checkout flow. Use `off_session` if your customer may or may not be in your checkout flow. For more, learn to save card details after a payment. Stripe uses setup_future_usage to dynamically optimize your payment flow and comply with regional legislation and network rules. For example, if your customer is impacted by SCA, using off_session will ensure that they are authenticated while processing this PaymentIntent. You will then be able to collect off-session payments for this customer. + /// - setupFutureUsage: Indicates that you intend to make future payments with this PaymentIntent’s payment method. If present, the payment method used with this PaymentIntent can be attached to a Customer, even after the transaction completes. Use `on_session` if you intend to only reuse the payment method when your customer is present in your checkout flow. Use `off_session` if your customer may or may not be in your checkout flow. For more, learn to save card details after a payment. Stripe uses `setup_future_usage` to dynamically optimize your payment flow and comply with regional legislation and network rules. For example, if your customer is impacted by SCA, using `off_session` will ensure that they are authenticated while processing this PaymentIntent. You will then be able to collect off-session payments for this customer. /// - shipping: Shipping information for this PaymentIntent. /// - statementDescriptor: For non-card charges, you can use this value as the complete description that appears on your customers’ statements. Must contain at least one letter, maximum 22 characters. /// - statementDescriptorSuffix: Provides information about a card payment that customers see on their statements. Concatenated with the prefix (shortened descriptor) or statement descriptor that’s set on the account to form the complete statement descriptor. Maximum 22 characters for the concatenated descriptor. diff --git a/Sources/StripeKit/Core Resources/Tokens/TokenRoutes.swift b/Sources/StripeKit/Core Resources/Tokens/TokenRoutes.swift index a5b59910..8e47b362 100644 --- a/Sources/StripeKit/Core Resources/Tokens/TokenRoutes.swift +++ b/Sources/StripeKit/Core Resources/Tokens/TokenRoutes.swift @@ -38,9 +38,19 @@ public protocol TokenRoutes { /// - Returns: A `StripeToken`. func create(account: [String: Any]) -> EventLoopFuture - /// Creates a single-use token that represents the details for a person. Use this when creating or updating persons associated with a Connect account. See the documentation to learn more. Person tokens may be created only in live mode, with your application’s publishable key. Your application’s secret key may be used to create person tokens only in test mode. + /// Creates a single-use token that represents the details for a person. Use this when creating or updating persons associated with a Connect account. See the documentation to learn more. + /// + /// Person tokens may be created only in live mode, with your application’s publishable key. Your application’s secret key may be used to create person tokens only in test mode. /// - Parameter person: Information for the person this token will represent. - func create(person: [String: Any]) -> EventLoopFuture + /// - Returns: A `StripeToken`. + func create(person: [String: Any]) -> EventLoopFuture + + /// Creates a single-use token that represents an updated CVC value to be used for CVC re-collection. This token can be used when confirming a card payment using a saved card on a PaymentIntent with `confirmation_method`: manual. + /// + /// For most cases, use our JavaScript library instead of using the API. For a `PaymentIntent` with `confirmation_method: automatic`, use our recommended payments integration without tokenizing the CVC value. + /// - Parameter cvcUpdate: The CVC value, in string form. + /// - Returns: A `StripeToken`. + func create(cvcUpdate: String) -> EventLoopFuture /// Retrieves the token with the given ID. /// @@ -54,27 +64,31 @@ public protocol TokenRoutes { extension TokenRoutes { public func create(card: Any? = nil, customer: String? = nil) -> EventLoopFuture { - return create(card: card, customer: customer) + create(card: card, customer: customer) } public func create(bankAcocunt: [String: Any]? = nil, customer: String? = nil) -> EventLoopFuture { - return create(bankAcocunt: bankAcocunt, customer: customer) + create(bankAcocunt: bankAcocunt, customer: customer) } public func create(pii: String) -> EventLoopFuture { - return create(pii: pii) + create(pii: pii) } public func create(account: [String: Any]) -> EventLoopFuture { - return create(account: account) + create(account: account) } - public func create(person: [String: Any]) -> EventLoopFuture { - return create(person: person) + public func create(person: [String: Any]) -> EventLoopFuture { + create(person: person) } public func retrieve(token: String) -> EventLoopFuture { - return retrieve(token: token) + retrieve(token: token) + } + + public func create(cvcUpdate: String) -> EventLoopFuture { + create(cvcUpdate: cvcUpdate) } } @@ -134,7 +148,7 @@ public struct StripeTokenRoutes: TokenRoutes { return apiHandler.send(method: .POST, path: tokens, body: .string(body.queryParameters), headers: headers) } - public func create(person: [String : Any]) -> EventLoopFuture { + public func create(person: [String : Any]) -> EventLoopFuture { var body: [String: Any] = [:] person.forEach { body["person[\($0)]"] = $1 } @@ -143,6 +157,12 @@ public struct StripeTokenRoutes: TokenRoutes { } public func retrieve(token: String) -> EventLoopFuture { - return apiHandler.send(method: .GET, path: "\(tokens)/\(token)", headers: headers) + apiHandler.send(method: .GET, path: "\(tokens)/\(token)", headers: headers) + } + + public func create(cvcUpdate: String) -> EventLoopFuture { + let body: [String: Any] = ["cvc_update": ["cvc": cvcUpdate]] + + return apiHandler.send(method: .POST, path: tokens, body: .string(body.queryParameters), headers: headers) } } diff --git a/Sources/StripeKit/EphemeralKey/EphemeralKey.swift b/Sources/StripeKit/EphemeralKey/EphemeralKey.swift index c42a0895..abcf243d 100644 --- a/Sources/StripeKit/EphemeralKey/EphemeralKey.swift +++ b/Sources/StripeKit/EphemeralKey/EphemeralKey.swift @@ -8,11 +8,19 @@ import Foundation public struct StripeEphemeralKey: StripeModel { + /// Unique identifier for the object. public var id: String + /// String representing the object's type. Objects of the same type share the same value. public var object: String public var associatedObjects: [[String : String]]? + /// Time at which the object was created. Measured in seconds since the Unix epoch. public var created: Date + /// Whether this object is deleted or not. + public var deleted: Bool? + /// Time at which the key will expire. Measured in seconds since the Unix epoch. public var expires: Date? + ///Has the value `true` if the object exists in live mode or the value `false` if the object exists in test mode. public var livemode: Bool? + /// The key's secret. You can use this value to make authorized requests to the Stripe API. public var secret: String? } diff --git a/Sources/StripeKit/Fraud/Early Fraud Warnings/EarlyFraudWarning.swift b/Sources/StripeKit/Fraud/Early Fraud Warnings/EarlyFraudWarning.swift index 65abec68..d5f4edb4 100644 --- a/Sources/StripeKit/Fraud/Early Fraud Warnings/EarlyFraudWarning.swift +++ b/Sources/StripeKit/Fraud/Early Fraud Warnings/EarlyFraudWarning.swift @@ -23,6 +23,8 @@ public struct StripeEarlyFraudWarning: StripeModel { public var fraudType: StripeEarlyFraudWarningFraudType? /// Has the value true if the object exists in live mode or the value false if the object exists in test mode. public var livemode: Bool? + /// ID of the Payment Intent this early fraud warning is for, optionally expanded. + @Expandable public var paymentIntent: String? } public enum StripeEarlyFraudWarningFraudType: String, StripeModel { diff --git a/Sources/StripeKit/Identity/VerificationReports/VerificationReport.swift b/Sources/StripeKit/Identity/VerificationReports/VerificationReport.swift new file mode 100644 index 00000000..13478d69 --- /dev/null +++ b/Sources/StripeKit/Identity/VerificationReports/VerificationReport.swift @@ -0,0 +1,217 @@ +// +// VerificationReport.swift +// StripeKit +// +// Created by Andrew Edwards on 7/24/21. +// + +import Foundation + +public struct StripeVerificationReport: StripeModel { + /// Unique identifier for the object. + public var id: String + /// String representing the object’s type. Objects of the same type share the same value. + public var object: String + /// Time at which the object was created. Measured in seconds since the Unix epoch. + public var created: Date + /// Result of the document check for this report. + public var document: StripeVerificationReportDocument? + /// Result of the id number check for this report. + public var idNumber: StripeVerificationReportIdNumber? + /// Has the value true if the object exists in live mode or the value false if the object exists in test mode. + public var livemode: Bool + /// Configuration options for this report. + public var options: StripeVerificationReportOptions? + /// Result of the selfie check for this report. + public var selfie: StripeVerificationReportSelfie? + /// Type of report. + public var type: StripeVerificationReportType? + /// ID of the VerificationSession that created this report. + public var verificationSession: String? +} + +public struct StripeVerificationReportDocument: StripeModel { + /// Address as it appears in the document. + public var address: StripeAddress? + /// Date of birth as it appears in the document. + /// This field is not included by default. To include it in the response, expand the `dob` field. + public var dob: StripePersonDOB? + /// Details on the verification error. Present when status is `unverified`. + public var error: StripeVerificationReportDocumentError? + /// Expiration date of the document. + /// This field is not included by default. To include it in the response, expand the `expiration_date` field. + public var expirationDate: StripePersonDOB? + /// Array of `File` ids containing images for this document. + public var files: [String]? + /// First name as it appears in the document. + public var firstName: String? + /// Issued date of the document. + public var issuedDate: StripePersonDOB? + /// Issuing country of the document. + public var issuingCountry: String? + /// Last name as it appears in the document. + public var lastName: String? + /// Document ID number. + /// This field is not included by default. To include it in the response, expand the `number` field. + public var number: String? + /// Status of this `document` check. + public var status: StripeVerificationReportDocumentStatus? + /// The type of the document. + public var type: StripeVerificationReportDocumentType? +} + +public struct StripeVerificationReportDocumentError: StripeModel { + /// A short machine-readable string giving the reason for the verification failure. + public var code: StripeVerificationReportDocumentErrorCode? + /// A human-readable message giving the reason for the failure. These messages can be shown to your users. + public var reason: String? +} + +public enum StripeVerificationReportDocumentErrorCode: String, StripeModel { + /// The provided identity document has expired. + case documentExpired = "document_expired" + /// Stripe couldn’t verify the provided identity document. See [list of supported document types](https://stripe.com/docs/identity/verification-checks?type=document) + case documentUnverifiedOther = "document_unverified_other" + /// The provided identity document isn’t one of the session’s [allowed document types](https://stripe.com/docs/api/identity/verification_sessions/create#create_identity_verification_session-options-document-allow_document_types) + case documentTypeNotSupported = "document_type_not_supported" +} + +public enum StripeVerificationReportDocumentStatus: String, StripeModel { + /// The check resulted in a successful verification. + case verified + /// The data being checked was not able to be verified. + case unverified +} + +public enum StripeVerificationReportDocumentType: String, StripeModel { + /// Drivers license document type. + case drivingLicense = "driving_license" + /// Passport document type. + case passport + /// ID card document type. + case idCard = "id_card" +} + +public struct StripeVerificationReportIdNumber: StripeModel { + /// Date of birth. + /// This field is not included by default. To include it in the response, expand the `dob` field. + public var dob: StripePersonDOB? + /// Details on the verification error. Present when status is `unverified`. + public var error: StripeVerificationReportIdNumberError? + /// First name. + public var firstName: String? + /// This field is not included by default. To include it in the response, expand the `id_number` field. + public var idNumber: String? + /// Type of ID number. + public var idNumberType: StripeVerificationReportIdNumberType? + /// Last name. + public var lastName: String? + /// Status of the `id_number` check. + public var status: StripeVerificationReportIdNumberStatus? +} + +public struct StripeVerificationReportIdNumberError: StripeModel { + /// A short machine-readable string giving the reason for the verification failure. + public var code: StripeVerificationReportIdNumberErrorReason? + /// A human-readable message giving the reason for the failure. These messages can be shown to your users. + public var reason: String? +} + +public enum StripeVerificationReportIdNumberErrorReason: String, StripeModel { + /// The information provided couldn’t be verified. See [list of supported ID numbers](https://stripe.com/docs/identity/verification-checks?type=id-number) + case idNumberUnverifiedOther = "id_number_unverified_other" + /// The provided document didn’t contain enough data to match against the ID number. + case idNumberInsufficientDocumentData = "id_number_insufficient_document_data" + /// The information provided couldn’t be matched against global databases. + case idNumberMismatch = "id_number_mismatch" +} + +public enum StripeVerificationReportIdNumberType: String, StripeModel { + /// An individual CPF number from Brazil. + case brCpf = "br_cpf" + /// A national registration identity card number from Singapore. + case sgNric = "sg_nric" + /// A social security number from the United States. + case usSsn = "us_ssn" +} + +public enum StripeVerificationReportIdNumberStatus: String, StripeModel { + /// The check resulted in a successful verification. + case verified + /// The data being checked was not able to be verified. + case unverified +} + +public struct StripeVerificationReportOptions: StripeModel { + /// Configuration options to apply to the `document` check. + public var document: StripeVerificationReportOptionsDocument? + /// Configuration options to apply to the `id_number` check. + public var idNumber: StripeVerificationReportOptionsIdNumber? +} + +public struct StripeVerificationReportOptionsDocument: StripeModel { + /// Array of strings of allowed identity document types. If the provided identity document isn’t one of the allowed types, the verification check will fail with a `document_type_not_allowed` error code. + public var allowedTypes: [StripeVerificationReportOptionsDocumentAllowedType]? + /// Collect an ID number and perform an [ID number check](https://stripe.com/docs/identity/verification-checks?type=id-number) with the document’s extracted name and date of birth. + public var requireIdNumber: Bool? + /// Disable image uploads, identity document images have to be captured using the device’s camera. + public var requireLiveCapture: Bool? + /// Capture a face image and perform a [selfie check](https://stripe.com/docs/identity/verification-checks?type=selfie) comparing a photo ID and a picture of your user’s face. Learn more. + public var requireMatchingSelfie: Bool +} + +public enum StripeVerificationReportOptionsDocumentAllowedType: String, StripeModel { + /// Drivers license document type. + case drivingLicense = "driving_license" + /// Passport document type. + case passport + /// ID card document type. + case idNumber = "id_number" +} + +public struct StripeVerificationReportOptionsIdNumber: StripeModel {} + +public struct StripeVerificationReportSelfie: StripeModel { + /// ID of the File holding the image of the identity document used in this check. + public var document: String? + /// Details on the verification error. Present when status is `unverified`. + public var error: StripeVerificationReportSelfieError? + /// ID of the File holding the image of the selfie used in this check. + public var selfie: String? + /// Status of this `selfie` check. + public var status: StripeVerificationReportSelfieStatus? +} + +public struct StripeVerificationReportSelfieError: StripeModel { + /// A short machine-readable string giving the reason for the verification failure. + public var code: StripeVerificationReportSelfieErrorCode? + /// A human-readable message giving the reason for the failure. These messages can be shown to your users. + public var reason: String? +} + +public enum StripeVerificationReportSelfieErrorCode: String, StripeModel { + case selfieDocumentMissingPhoto = "selfie_document_missing_photo" + case selfieFaceMismatch = "selfie_face_mismatch" + case selfieUnverifiedOther = "selfie_unverified_other" +} + +public enum StripeVerificationReportSelfieStatus: String, StripeModel { + /// The check resulted in a successful verification. + case verified + /// The data being checked was not able to be verified. + case unverified +} + +public enum StripeVerificationReportType: String, StripeModel { + /// Perform a document check. + case document + /// Perform an ID number check. + case idNumber = "id_number" +} + +public struct StripeVerificationReportList: StripeModel { + public var object: String + public var data: [StripeVerificationReport]? + public var hasMore: Bool? + public var url: String? +} diff --git a/Sources/StripeKit/Identity/VerificationReports/VerificationReportRoutes.swift b/Sources/StripeKit/Identity/VerificationReports/VerificationReportRoutes.swift new file mode 100644 index 00000000..4946ef17 --- /dev/null +++ b/Sources/StripeKit/Identity/VerificationReports/VerificationReportRoutes.swift @@ -0,0 +1,65 @@ +// +// VerificationReportRoutes.swift +// StripeKit +// +// Created by Andrew Edwards on 7/24/21. +// + +import NIO +import NIOHTTP1 + +public protocol VerificationReportRoutes { + /// Retrieves an existing VerificationReport + /// - Parameter verificationReportId: The id of the verification report. + /// + /// - Returns: A `StripeVerificationReport` + func retrieve(verificationReportId: String, expand: [String]?) -> EventLoopFuture + + /// List all verification reports. + /// - Parameter filter: A dictionary that will be used for the query parameters. + /// + /// - Returns: A `StripeVerificationReportList` + func listAll(filter: [String: Any]?) -> EventLoopFuture + + /// Headers to send with the request. + var headers: HTTPHeaders { get set } +} + +extension VerificationReportRoutes { + func retrieve(verificationReportId: String, expand: [String]? = nil) -> EventLoopFuture { + retrieve(verificationReportId: verificationReportId, expand: expand) + } + + func listAll(filter: [String: Any]? = nil) -> EventLoopFuture { + listAll(filter: filter) + } +} + +public struct StripeVerificationReportRoutes: VerificationReportRoutes { + public var headers: HTTPHeaders = [:] + + private let apiHandler: StripeAPIHandler + private let verificationreport = APIBase + APIVersion + "identity/verification_reports" + + init(apiHandler: StripeAPIHandler) { + self.apiHandler = apiHandler + } + + public func retrieve(verificationReportId: String, expand: [String]?) -> EventLoopFuture { + var queryParams = "" + if let expand = expand { + queryParams = ["expand": expand].queryParameters + } + + return apiHandler.send(method: .GET, path: verificationreport + "/\(verificationReportId)", query: queryParams, headers: headers) + } + + public func listAll(filter: [String: Any]?) -> EventLoopFuture { + var queryParams = "" + if let filter = filter { + queryParams = filter.queryParameters + } + + return apiHandler.send(method: .GET, path: verificationreport, query: queryParams, headers: headers) + } +} diff --git a/Sources/StripeKit/Identity/VerificationSession/VerificationSession.swift b/Sources/StripeKit/Identity/VerificationSession/VerificationSession.swift new file mode 100644 index 00000000..d95ffbfb --- /dev/null +++ b/Sources/StripeKit/Identity/VerificationSession/VerificationSession.swift @@ -0,0 +1,173 @@ +// +// VerificationSession.swift +// StripeKit +// +// Created by Andrew Edwards on 7/24/21. +// + +import Foundation + +public struct StripeVerificationSession: StripeModel { + /// Unique identifier for the object. + public var id: String + /// String representing the object’s type. Objects of the same type share the same value. + public var object: String + /// The short-lived client secret used by Stripe.js to show a verification modal inside your app. This client secret expires after 24 hours and can only be used once. Don’t store it, log it, embed it in a URL, or expose it to anyone other than the user. Make sure that you have TLS enabled on any page that includes the client secret. Refer to our docs on passing the client secret to the frontend to learn more. + public var clientSecret: String? + /// Time at which the object was created. Measured in seconds since the Unix epoch. + public var created: Date + /// If present, this property tells you the last error encountered when processing the verification. + public var lastError: StripeVerificationSessionLastError? + /// ID of the most recent VerificationReport. [Learn more about accessing detailed verification results](https://stripe.com/docs/identity/verification-sessions#results) + @Expandable public var lastVerificationReport: String? + /// Has the value true if the object exists in live mode or the value false if the object exists in test mode. + public var livemode: Bool? + /// Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. + public var metadata: [String: String]? + /// A set of options for the session’s verification checks. + public var options: StripeVerificationSessionOptions? + /// Redaction status of this VerificationSession. If the VerificationSession is not redacted, this field will be null. + public var redaction: StripeVerificationSessionRedaction? + /// Status of this VerificationSession. [Learn more about the lifecycle of sessions](https://stripe.com/docs/identity/how-sessions-work) + public var status: StripeVerificationSessionStatus? + /// The type of [verification check](https://stripe.com/docs/identity/verification-checks) to be performed. + public var type: StripeVerificationSessionType? + /// The short-lived URL that you use to redirect a user to Stripe to submit their identity information. This URL expires after 24 hours and can only be used once. Don’t store it, log it, send it in emails or expose it to anyone other than the user. Refer to our docs on [verifying identity documents](https://stripe.com/docs/identity/verify-identity-documents?platform=web&type=redirect) to learn how to redirect users to Stripe. + public var url: String? + /// The user’s verified data. + /// This field is not included by default. To include it in the response, expand the `verified_outputs` field. + public var verifiedOutputs: StripeVerificationSessionVerifiedOutputs? +} + +public struct StripeVerificationSessionLastError: StripeModel { + /// A short machine-readable string giving the reason for the verification or user-session failure. + public var code: StripeVerificationSessionLastErrorCode? + /// A message that explains the reason for verification or user-session failure. + public var reason: String? +} + +public enum StripeVerificationSessionLastErrorCode: String, StripeModel { + /// The user declined to be verified by Stripe. Check with your legal counsel to see if you have an obligation to offer an alternative, non-biometric means to verify, such as through a manual review. + case consentDeclined = "consent_declined" + /// The user’s device didn’t have a camera or they declined to grant Stripe permission to access it. + case deviceNotSupported = "device_not_supported" + /// The user began the verification but didn’t submit it. + case abandoned + /// Stripe does not verify users under the age of 16. + case underSupportedAge = "under_supported_age" + /// Stripe does not verify users from the provided country. + case countryNotSupported = "country_not_supported" + /// The provided identity document has expired. + case documentExpired = "document_expired" + /// Stripe couldn’t verify the provided identity document. See list of supported document types. + case documentUnverifiedOther = "document_unverified_other" + /// The provided identity document isn’t one of the session’s allowed document types. + case documentTypeNotSupported = "document_type_not_supported" + /// The provided identity document didn’t contain a picture of a face. + case selfieDocumentMissingPhoto = "selfie_document_missing_photo" + /// The captured face image didn’t match with the document’s face. + case selfieFaceMismatch = "selfie_face_mismatch" + /// Stripe couldn’t verify the provided selfie. + case selfieUnverifiedOther = "selfie_unverified_other" + /// The captured face image was manipulated. + case selfieManipulated = "selfie_manipulated" + /// The information provided couldn’t be verified. See list of supported ID numbers. + case idNumberUnverifiedOther = "id_number_unverified_other" + /// The provided document didn’t contain enough data to match against the ID number. + case idNumberInsufficientDocumentData = "id_number_insufficient_document_data" + /// The information provided couldn’t be matched against global databases. + case idNumberMismatch = "id_number_mismatch" +} + +public struct StripeVerificationSessionOptions: StripeModel { + /// Configuration options to apply to the `document` check. + public var document: StripeVerificationSessionOptionsDocument? + /// Configuration options to apply to the `id_number` check. + public var idNumber: StripeVerificationSessionOptionsIdNumber? +} + +public struct StripeVerificationSessionOptionsDocument: StripeModel { + /// Array of strings of allowed identity document types. If the provided identity document isn’t one of the allowed types, the verification check will fail with a `document_type_not_allowed` error code. + public var allowedTypes: [StripeVerificationSessionOptionsDocumentAllowedType]? + /// Collect an ID number and perform an [ID number check](https://stripe.com/docs/identity/verification-checks?type=id-number) with the document’s extracted name and date of birth. + public var requireIdNumber: Bool? + /// Disable image uploads, identity document images have to be captured using the device’s camera. + public var requireLiveCapture: Bool? + /// Capture a face image and perform a [selfie check](https://stripe.com/docs/identity/verification-checks?type=selfie) comparing a photo ID and a picture of your user’s face. Learn more. + public var requireMatchingSelfie: Bool +} + +public enum StripeVerificationSessionOptionsDocumentAllowedType: String, StripeModel { + /// Drivers license document type. + case drivingLicense = "driving_license" + /// Passport document type. + case passport + /// ID card document type. + case idNumber = "id_number" +} + +public struct StripeVerificationSessionOptionsIdNumber: StripeModel {} + +public struct StripeVerificationSessionRedaction: StripeModel { + /// Indicates whether this object and its related objects have been redacted or not. + public var status: StripeVerificationSessionRedactionStatus? +} + +public enum StripeVerificationSessionRedactionStatus: String, StripeModel { + /// This object and its related objects have been redacted. + case redacted + /// This object has been redacted, and its related objects are in the process of being redacted. This process may take up to four days. + case processing +} + +public enum StripeVerificationSessionStatus: String, StripeModel { + /// Requires user input before processing can continue. + case requiresInput = "requires_input" + /// The session has been submitted and is being processed. Most [verification checks](https://stripe.com/docs/identity/verification-checks) take a few minutes to process. + case processing + /// Processing of all the verification checks are complete and successfully verified. + case verified + /// The VerificationSession has been invalidated for future submission attempts. + case canceled +} + +public enum StripeVerificationSessionType: String, StripeModel { + /// [Document check](https://stripe.com/docs/identity/verification-checks?type=document) + case document + /// [ID number check](https://stripe.com/docs/identity/verification-checks?type=id-number). + case idNumber = "id_number" +} + +public struct StripeVerificationSessionVerifiedOutputs: StripeModel { + /// The user’s verified address. + public var address: StripeAddress? + /// The user’s verified date of birth. + /// This field is not included by default. To include it in the response, expand the `dob` field. + public var dob: StripePersonDOB? + /// The user’s verified first name. + public var firstName: String? + /// The user’s verified id number. + /// This field is not included by default. To include it in the response, expand the `id_number` field. + public var idNumber: String? + /// The user’s verified id number type. + public var idNumberType: StripeVerificationSessionVerifiedOutputsIdNumberType? + /// The user’s verified last name. + public var lastName: String? +} + +public enum StripeVerificationSessionVerifiedOutputsIdNumberType: String, StripeModel { + /// An individual CPF number from Brazil. + case brCpf = "br_cpf" + /// A national registration identity card number from Singapore. + case sgNric = "sg_nric" + /// A social security number from the United States. + case usSsn = "us_ssn" +} + + +public struct StripeVerificationSessionList: StripeModel { + public var object: String + public var data: [StripeVerificationSession]? + public var hasMore: Bool? + public var url: String? +} diff --git a/Sources/StripeKit/Identity/VerificationSession/VerificationSessionRoutes.swift b/Sources/StripeKit/Identity/VerificationSession/VerificationSessionRoutes.swift new file mode 100644 index 00000000..f6cc26aa --- /dev/null +++ b/Sources/StripeKit/Identity/VerificationSession/VerificationSessionRoutes.swift @@ -0,0 +1,222 @@ +// +// VerificationSessionRoutes.swift +// StripeKit +// +// Created by Andrew Edwards on 7/24/21. +// + +import NIO +import NIOHTTP1 + +public protocol VerificationSessionRoutes { + /// Creates a VerificationSession object. + /// After the VerificationSession is created, display a verification modal using the session `client_secret` or send your users to the session’s url. + /// If your API key is in test mode, verification checks won’t actually process, though everything else will occur as if in live mode. + /// - Parameter type: The type of verification check to be performed. + /// - Parameter metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata. + /// - Parameter options: A set of options for the session’s verification checks. + /// - Parameter returnUrl: The URL that the user will be redirected to upon completing the verification flow. + /// - Parameter expand: An array of properties to expand. + /// + /// - Returns: A `StripeVerificationSession`. + func create(type: StripeVerificationSessionType, + metadata: [String: String]?, + options: [String: Any]?, + returnUrl: String?, + expand: [String]?) -> EventLoopFuture + + /// Returns a list of VerificationSessions + /// - Parameter filter: A dictionary that will be used for the query parameters. + /// - Returns: A `StripeVerificationSessionList`. + func listAll(filter: [String: Any]?) -> EventLoopFuture + + + /// Retrieves the details of a VerificationSession that was previously created. + /// When the session status is `requires_input`, you can use this method to retrieve a valid `client_secret` or url to allow re-submission. + /// - Parameter verificationSessionId: Id of the verification session. + /// - Parameter expand: An array of properties to expand. + /// + /// - Returns: A `StripeVerificationSession`. + func retrieve(verificationSessionId: String, expand: [String]?) -> EventLoopFuture + + /// Updates a VerificationSession object. + /// When the session status is `requires_input`, you can use this method to update the verification check and options. + /// - Parameter verificationSessionId: Id of the verification session. + /// - Parameter metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata. + /// - Parameter options: A set of options for the session’s verification checks. + /// - Parameter type: The type of verification check to be performed. + /// - Parameter expand: An array of properties to expand. + /// + /// - Returns: A `StripeVerificationSession`. + func update(verificationSessionId: String, + metadata: [String: String]?, + options: [String: Any]?, + type: StripeVerificationSessionType?, + expand: [String]?) -> EventLoopFuture + + + /// A VerificationSession object can be canceled when it is in `requires_input` status. + /// Once canceled, future submission attempts are disabled. This cannot be undone. + /// - Parameter verificationSessionId: Id of the verification session. + /// - Parameter expand: An array of properties to expand. + /// + /// - Returns: The canceled `StripeVerificationSession`. + func cancel(verificationSessionId: String, expand: [String]?) -> EventLoopFuture + + /// Redact a VerificationSession to remove all collected information from Stripe. This will redact the VerificationSession and all objects related to it, including VerificationReports, Events, request logs, etc. + /// + /// A VerificationSession object can be redacted when it is in `requires_input` or verified status. Redacting a VerificationSession in `requires_action` state will automatically cancel it. + /// + /// The redaction process may take up to four days. When the redaction process is in progress, the VerificationSession’s `redaction.status` field will be set to `processing`; when the process is finished, it will change to `redacted` and an `identity.verification_session.redacted` event will be emitted. + /// + /// Redaction is irreversible. Redacted objects are still accessible in the Stripe API, but all the fields that contain personal data will be replaced by the string [redacted] or a similar placeholder. The metadata field will also be erased. Redacted objects cannot be updated or used for any purpose. + /// - Parameter verificationSessionId: Id of the verification session. + /// - Parameter expand: An array of properties to expand. + /// + /// - Returns: The redacted `StripeVerificationSession`. + func redact(verificationSessionId: String, expand: [String]?) -> EventLoopFuture + + /// Headers to send with the request. + var headers: HTTPHeaders { get set } +} + +extension VerificationSessionRoutes { + func create(type: StripeVerificationSessionType, + metadata: [String: String]? = nil, + options: [String: Any]? = nil, + returnUrl: String? = nil, + expand: [String]? = nil) -> EventLoopFuture { + create(type: type, + metadata: metadata, + options: options, + returnUrl: returnUrl, + expand: expand) + } + + func listAll(filter: [String: Any]? = nil) -> EventLoopFuture { + listAll(filter: filter) + } + + func retrieve(verificationSessionId: String, expand: [String]? = nil) -> EventLoopFuture { + retrieve(verificationSessionId: verificationSessionId, expand: expand) + } + + func update(verificationSessionId: String, + metadata: [String: String]? = nil, + options: [String: Any]? = nil, + type: StripeVerificationSessionType? = nil, + expand: [String]? = nil) -> EventLoopFuture { + update(verificationSessionId: verificationSessionId, metadata: metadata, options: options, type: type, expand: expand) + } + + func cancel(verificationSessionId: String, expand: [String]? = nil) -> EventLoopFuture { + cancel(verificationSessionId: verificationSessionId, expand: expand) + } + + func redact(verificationSessionId: String, expand: [String]? = nil) -> EventLoopFuture { + redact(verificationSessionId: verificationSessionId, expand: expand) + } +} + +public struct StripeVerificationSessionRoutes: VerificationSessionRoutes { + public var headers: HTTPHeaders = [:] + + private let apiHandler: StripeAPIHandler + private let verificationsession = APIBase + APIVersion + "identity/verification_sessions" + + init(apiHandler: StripeAPIHandler) { + self.apiHandler = apiHandler + } + + public func create(type: StripeVerificationSessionType, + metadata: [String: String]?, + options: [String: Any]?, + returnUrl: String?, + expand: [String]?) -> EventLoopFuture { + var body: [String: Any] = ["type": type.rawValue] + + if let metadata = metadata { + metadata.forEach { body["metadata[\($0)]"] = $1 } + } + + if let options = options { + options.forEach { body["options[\($0)]"] = $1 } + } + + if let returnUrl = returnUrl { + body["return_url"] = returnUrl + } + + if let expand = expand { + body["expand"] = expand + } + + return apiHandler.send(method: .POST, path: verificationsession, body: .string(body.queryParameters), headers: headers) + } + + public func listAll(filter: [String: Any]?) -> EventLoopFuture { + var queryParams = "" + if let filter = filter { + queryParams = filter.queryParameters + } + + return apiHandler.send(method: .GET, path: verificationsession, query: queryParams, headers: headers) + } + + public func retrieve(verificationSessionId: String, expand: [String]?) -> EventLoopFuture { + var queryParams = "" + if let expand = expand { + queryParams = ["expand": expand].queryParameters + } + + return apiHandler.send(method: .GET, path: "\(verificationsession)/\(verificationSessionId)", query: queryParams, headers: headers) + } + + public func update(verificationSessionId: String, + metadata: [String: String]?, + options: [String: Any]?, + type: StripeVerificationSessionType?, + expand: [String]?) -> EventLoopFuture { + var body: [String: Any] = [:] + + if let metadata = metadata { + metadata.forEach { body["metadata[\($0)]"] = $1 } + } + + if let options = options { + options.forEach { body["options[\($0)]"] = $1 } + } + + if let expand = expand { + body["expand"] = expand + } + + if let type = type { + body["type"] = type.rawValue + } + + if let expand = expand { + body["expand"] = expand + } + + return apiHandler.send(method: .POST, path: "\(verificationsession)/\(verificationSessionId)", body: .string(body.queryParameters), headers: headers) + } + + public func cancel(verificationSessionId: String, expand: [String]?) -> EventLoopFuture { + var queryParams = "" + if let expand = expand { + queryParams = ["expand": expand].queryParameters + } + + return apiHandler.send(method: .POST, path: "\(verificationsession)/\(verificationSessionId)/cancel", query: queryParams, headers: headers) + } + + public func redact(verificationSessionId: String, expand: [String]?) -> EventLoopFuture { + var queryParams = "" + if let expand = expand { + queryParams = ["expand": expand].queryParameters + } + + return apiHandler.send(method: .POST, path: "\(verificationsession)/\(verificationSessionId)/redact", query: queryParams, headers: headers) + } +} diff --git a/Sources/StripeKit/Issuing/Authorizations/Authorization.swift b/Sources/StripeKit/Issuing/Authorizations/Authorization.swift index 03657e93..c7a2c15b 100644 --- a/Sources/StripeKit/Issuing/Authorizations/Authorization.swift +++ b/Sources/StripeKit/Issuing/Authorizations/Authorization.swift @@ -82,9 +82,11 @@ public enum StripeAuthorizationMethod: String, StripeModel { } public struct StripeAuthorizationMerchantData: StripeModel { - // TODO: - Make an enum once it's solidified. https://stripe.com/docs/issuing/merchant-categories + // TODO: - Make this an enum once it's solidified. https://stripe.com/docs/issuing/merchant-categories /// A categorization of the seller’s type of business. See our merchant categories guide for a list of possible values. public var category: String? + /// The merchant category code for the seller’s business + public var categoryCode: String? /// City where the seller is located public var city: String? /// Country where the seller is located diff --git a/Sources/StripeKit/Issuing/Transactions/Transaction.swift b/Sources/StripeKit/Issuing/Transactions/Transaction.swift index e1c9aa71..b39d0f4e 100644 --- a/Sources/StripeKit/Issuing/Transactions/Transaction.swift +++ b/Sources/StripeKit/Issuing/Transactions/Transaction.swift @@ -42,6 +42,8 @@ public struct StripeTransaction: StripeModel { public var type: StripeTransactionType? /// Additional purchase information that is optionally provided by the merchant. This field is not included by default. To include it in the response, expand the `purchase_details` field. public var purchaseDetails: StripeTransactionPurchaseDetails? + /// The digital wallet used for this transaction. One of `apple_pay`, `google_pay`, or `samsung_pay`. + public var wallet: StripeTransactionWallet? } public struct StripeTransactionAmountDetails: StripeModel { @@ -145,3 +147,9 @@ public struct StripeTransactionPurchaseDetailsReceipt: StripeModel { /// The unit cost of the item in cents. public var unitCost: Int? } + +public enum StripeTransactionWallet: String, StripeModel { + case applePay = "apple_pay" + case googlePay = "google_pay" + case samsungPay = "samsung_pay" +} diff --git a/Sources/StripeKit/Payment Methods/Bank Accounts/BankAccount.swift b/Sources/StripeKit/Payment Methods/Bank Accounts/BankAccount.swift index 59169986..3dfa0818 100644 --- a/Sources/StripeKit/Payment Methods/Bank Accounts/BankAccount.swift +++ b/Sources/StripeKit/Payment Methods/Bank Accounts/BankAccount.swift @@ -14,6 +14,8 @@ public struct StripeBankAccount: StripeModel { public var object: String /// The account this bank account belongs to. @Expandable public var account: String? + /// The bank account type. This can only be `checking` or `savings` in most countries. In Japan, this can only be `futsu` or `toza`. + public var accountType: String? /// The name of the person or business that owns the bank account. public var accountHolderName: String? /// The type of entity that holds the account. This can be either `individual` or `company`. diff --git a/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethod.swift b/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethod.swift index 0eb8901e..54a2add9 100644 --- a/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethod.swift +++ b/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethod.swift @@ -13,6 +13,8 @@ public struct StripePaymentMethod: StripeModel { public var id: String /// String representing the object’s type. Objects of the same type share the same value. public var object: String + /// If this is an `acss_debit` PaymentMethod, this hash contains details about the ACSS Debit payment method. + public var acssDebit: StripePaymentMethodAcssDebit? /// If this is an AfterpayClearpay PaymentMethod, this hash contains details about the AfterpayClearpay payment method. public var afterpayClearpay: StripePaymentMethodAfterpayClearpay? /// If this is an Alipay PaymentMethod, this hash contains details about the Alipay payment method. @@ -23,6 +25,8 @@ public struct StripePaymentMethod: StripeModel { public var bacsDebit: StripePaymentMethodBacsDebit? /// If this is a `bancontact` PaymentMethod, this hash contains details about the Bancontact payment method. public var bancontact: StripePaymentMethodBancontact? + /// If this is a `boleto` PaymentMethod, this hash contains details about the Boleto payment method. + public var boleto: StripePaymentMethodBoleto? /// Billing information associated with the PaymentMethod that may be used or required by particular types of payment methods. public var billingDetails: StripeBillingDetails? /// If this is a `card` PaymentMethod, this hash contains details about the card. @@ -39,6 +43,8 @@ public struct StripePaymentMethod: StripeModel { public var fpx: StripePaymentMethodFpx? /// If this is an `giropay` PaymentMethod, this hash contains details about the Giropay payment method. public var giropay: StripePaymentMethodGiropay? + /// If this is a `grabpay` PaymentMethod, this hash contains details about the GrabPay payment method. + public var grabpay: StripePaymentMethodGrabpay? /// If this is an `ideal` PaymentMethod, this hash contains details about the iDEAL payment method. public var ideal: StripePaymentMethodIdeal? /// Has the value `true` if the object exists in live mode or the value `false` if the object exists in test mode. @@ -53,10 +59,46 @@ public struct StripePaymentMethod: StripeModel { public var sepaDebit: StripePaymentMethodSepaDebit? /// If this is a sofort PaymentMethod, this hash contains details about the SOFORT payment method. public var sofort: StripePaymentMethodSofort? - /// The type of the PaymentMethod, one of `card` or `card_present`. An additional hash is included on the PaymentMethod with a name matching this value. It contains additional information specific to the PaymentMethod type. + /// If this is an `wechat_pay` PaymentMethod, this hash contains details about the `wechat_pay` payment method. + public var wechatPay: StripePaymentMethodWechatPay? + /// The type of the PaymentMethod. An additional hash is included on the PaymentMethod with a name matching this value. It contains additional information specific to the PaymentMethod type. public var type: StripePaymentMethodType? } +public enum StripePaymentMethodType: String, StripeModel { + case acssDebit = "acss_debit" + case afterpayClearpay = "afterpay_clearpay" + case alipay + case auBecsDebit = "au_becs_debit" + case bacsDebit = "bacs_debit" + case bancontact + case boleto + case card + case eps + case fpx + case giropay + case grabpay + case ideal + case oxxo + case p24 + case sepaDebit = "sepa_debit" + case sofort + case wechatPay = "wechat_pay" +} + +public struct StripePaymentMethodAcssDebit: StripeModel { + /// Name of the bank associated with the bank account. + public var bankName: String? + /// Uniquely identifies this particular bank account. You can use this attribute to check whether two bank accounts are the same. + public var fingerprint: String? + /// Institution number of the bank account. + public var institutionNumber: String? + /// Last four digits of the bank account number. + public var last4: String? + /// Transit number of the bank account. + public var transitNumber: String? +} + public struct StripePaymentMethodAfterpayClearpay: StripeModel { // https://stripe.com/docs/api/payment_methods/object#payment_method_object-afterpay_clearpay } @@ -87,6 +129,11 @@ public struct StripePaymentMethodBancontact: StripeModel { // https://stripe.com/docs/api/payment_methods/object#payment_method_object-bancontact } +public struct StripePaymentMethodBoleto: StripeModel { + /// Uniquely identifies this customer tax_id (CNPJ or CPF) + public var taxId: String? +} + public struct StripePaymentMethodCard: StripeModel { /// Card brand. Can be `amex`, `diners`, `discover`, `jcb`, `mastercard`, `unionpay`, `visa`, or `unknown`. public var brand: StripePaymentMethodCardBrand? @@ -219,28 +266,26 @@ public struct StripePaymentMethodCardWalletVisaCheckout: StripeModel { public var shippingAddress: StripeAddress? } -public enum StripePaymentMethodType: String, StripeModel { - case afterpayClearpay = "afterpay_clearpay" - case alipay - case auBecsDebit = "au_becs_debit" - case bacsDebit = "bacs_debit" - case bancontact - case card - case eps - case fpx - case giropay - case grabpay - case ideal - case oxxo - case p24 - case sepaDebit = "sepa_debit" - case sofort -} - public struct StripePaymentMethodCardPresent: StripeModel { // https://stripe.com/docs/api/payment_methods/object#payment_method_object-card_present } +public struct StripePaymentMethodEps: StripeModel { + // https://stripe.com/docs/api/payment_methods/object#payment_method_object-eps +} + +public struct StripePaymentMethodFpx: StripeModel { + // https://stripe.com/docs/api/payment_methods/object#payment_method_object-fpx +} + +public struct StripePaymentMethodGiropay: StripeModel { + // https://stripe.com/docs/api/payment_methods/object#payment_method_object-giropay +} + +public struct StripePaymentMethodGrabpay: StripeModel { + // https://stripe.com/docs/api/payment_methods/object#payment_method_object-grabpay +} + public struct StripePaymentMethodIdeal: StripeModel { /// The customer’s bank, if provided. Can be one of `abn_amro`, `asn_bank`, `bunq`, `handelsbanken`, `ing`, `knab`, `moneyou`, `rabobank`, `regiobank`, `sns_bank`, `triodos_bank`, or `van_lanschot`. public var bank: StripePaymentMethodIdealBank? @@ -263,22 +308,6 @@ public enum StripePaymentMethodIdealBank: String, StripeModel { case vanLanschot = "van_lanschot" } -public struct StripePaymentMethodEps: StripeModel { - // https://stripe.com/docs/api/payment_methods/object#payment_method_object-eps -} - -public struct StripePaymentMethodFpx: StripeModel { - // https://stripe.com/docs/api/payment_methods/object#payment_method_object-fpx -} - -public struct StripePaymentMethodGiropay: StripeModel { - // https://stripe.com/docs/api/payment_methods/object#payment_method_object-giropay -} - -public struct StripePaymentMethodGrabpay: StripeModel { - // https://stripe.com/docs/api/payment_methods/object#payment_method_object-grabpay -} - public struct StripePaymentMethodOXXO: StripeModel { // https://stripe.com/docs/api/payment_methods/object#payment_method_object-oxxo } @@ -321,6 +350,10 @@ public struct StripePaymentMethodSepaDebit: StripeModel { public var last4: String? } +public struct StripePaymentMethodWechatPay: StripeModel { + // https://stripe.com/docs/api/payment_methods/object#payment_method_object-wechat_pay +} + public struct StripePaymentMethodList: StripeModel { public var object: String public var data: [StripePaymentMethod]? diff --git a/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethodRoutes.swift b/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethodRoutes.swift index 6403e4d3..7511e610 100644 --- a/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethodRoutes.swift +++ b/Sources/StripeKit/Payment Methods/PaymentMethods/PaymentMethodRoutes.swift @@ -14,11 +14,13 @@ public protocol PaymentMethodRoutes { /// - Parameters: /// - type: The type of the PaymentMethod. An additional hash is included on the PaymentMethod with a name matching this value. It contains additional information specific to the PaymentMethod type. Required unless `payment_method` is specified (see the Shared PaymentMethods guide) /// - billingDetails: Billing information associated with the PaymentMethod that may be used or required by particular types of payment methods. + /// - acssDebit: If this is an `acss_debit` PaymentMethod, this hash contains details about the ACSS Debit payment method. /// - afterpayClearpay: If this is an AfterpayClearpay PaymentMethod, this hash contains details about the AfterpayClearpay payment method. /// - alipay: If this is an Alipay PaymentMethod, this hash contains details about the Alipay payment method. /// - auBecsDebit: If this is an `au_becs_debit` PaymentMethod, this hash contains details about the bank account. /// - bacsDebit: If this is a `bacs_debit` PaymentMethod, this hash contains details about the Bacs Direct Debit bank account. /// - bancontact: If this is a `bancontact` PaymentMethod, this hash contains details about the Bancontact payment method. + /// - boleto: If this is a `boleto` PaymentMethod, this hash contains details about the Boleto payment method. /// - eps: If this is an `eps` PaymentMethod, this hash contains details about the EPS payment method. /// - fpx: If this is an `fpx` PaymentMethod, this hash contains details about the FPX payment method. /// - giropay: If this is an `giropay` PaymentMethod, this hash contains details about the Giropay payment method. @@ -30,15 +32,18 @@ public protocol PaymentMethodRoutes { /// - metadata: Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. /// - sepaDebit: If this is a `sepa_debit` PaymentMethod, this hash contains details about the SEPA debit bank account. /// - sofort: If this is a sofort PaymentMethod, this hash contains details about the SOFORT payment method. + /// - wechatPay: If this is a `wechat_pay` PaymentMethod, this hash contains details about the `wechat_pay` payment method. /// - expand: An array of properties to expand. /// - Returns: A `StripePaymentMethod`. func create(type: StripePaymentMethodType, billingDetails: [String: Any]?, + acssDebit: [String: Any]?, afterpayClearpay: [String: Any]?, alipay: [String: Any]?, auBecsDebit: [String: Any]?, bacsDebit: [String: Any]?, bancontact: [String: Any]?, + boleto: [String: Any]?, eps: [String: Any]?, fpx: [String: Any]?, giropay: [String: Any]?, @@ -50,6 +55,7 @@ public protocol PaymentMethodRoutes { metadata: [String: String]?, sepaDebit: [String: Any]?, sofort: [String: Any]?, + wechatPay: [String: Any]?, expand: [String]?) -> EventLoopFuture /// Retrieves a PaymentMethod object. @@ -110,11 +116,13 @@ public protocol PaymentMethodRoutes { extension PaymentMethodRoutes { public func create(type: StripePaymentMethodType, billingDetails: [String: Any]? = nil, + acssDebit: [String: Any]? = nil, afterpayClearpay: [String: Any]? = nil, alipay: [String: Any]? = nil, auBecsDebit: [String: Any]? = nil, bacsDebit: [String: Any]? = nil, bancontact: [String: Any]? = nil, + boleto: [String: Any]? = nil, eps: [String: Any]? = nil, fpx: [String: Any]? = nil, giropay: [String: Any]? = nil, @@ -126,14 +134,17 @@ extension PaymentMethodRoutes { metadata: [String: String]? = nil, sepaDebit: [String: Any]? = nil, sofort: [String: Any]? = nil, + wechatPay: [String: Any]? = nil, expand: [String]? = nil) -> EventLoopFuture { return create(type: type, billingDetails: billingDetails, + acssDebit: acssDebit, afterpayClearpay: afterpayClearpay, alipay: alipay, auBecsDebit: auBecsDebit, bacsDebit: bacsDebit, bancontact: bancontact, + boleto: boleto, eps: eps, fpx: fpx, giropay: giropay, @@ -145,6 +156,7 @@ extension PaymentMethodRoutes { metadata: metadata, sepaDebit: sepaDebit, sofort: sofort, + wechatPay: wechatPay, expand: expand) } @@ -167,9 +179,7 @@ extension PaymentMethodRoutes { public func listAll(customer: String, type: StripePaymentMethodType, filter: [String: Any]? = nil) -> EventLoopFuture { - return listAll(customer: customer, - type: type, - filter: filter) + return listAll(customer: customer, type: type, filter: filter) } public func attach(paymentMethod: String, customer: String, expand: [String]? = nil) -> EventLoopFuture { @@ -193,11 +203,13 @@ public struct StripePaymentMethodRoutes: PaymentMethodRoutes { public func create(type: StripePaymentMethodType, billingDetails: [String: Any]?, + acssDebit: [String: Any]?, afterpayClearpay: [String: Any]?, alipay: [String: Any]?, auBecsDebit: [String: Any]?, bacsDebit: [String: Any]?, bancontact: [String: Any]?, + boleto: [String: Any]?, eps: [String: Any]?, fpx: [String: Any]?, giropay: [String: Any]?, @@ -209,6 +221,7 @@ public struct StripePaymentMethodRoutes: PaymentMethodRoutes { metadata: [String: String]?, sepaDebit: [String: Any]?, sofort: [String: Any]?, + wechatPay: [String: Any]?, expand: [String]?) -> EventLoopFuture { var body: [String: Any] = ["type": type.rawValue] @@ -216,6 +229,10 @@ public struct StripePaymentMethodRoutes: PaymentMethodRoutes { billingDetails.forEach { body["billing_details[\($0)]"] = $1 } } + if let acssDebit = acssDebit { + acssDebit.forEach { body["acss_debit[\($0)]"] = $1 } + } + if let afterpayClearpay = afterpayClearpay { afterpayClearpay.forEach { body["afterpay_clearpay[\($0)]"] = $1 } } @@ -236,6 +253,10 @@ public struct StripePaymentMethodRoutes: PaymentMethodRoutes { bancontact.forEach { body["bancontact[\($0)]"] = $1 } } + if let boleto = boleto { + boleto.forEach { body["boleto[\($0)]"] = $1 } + } + if let eps = eps { eps.forEach { body["eps[\($0)]"] = $1 } } @@ -280,6 +301,10 @@ public struct StripePaymentMethodRoutes: PaymentMethodRoutes { sofort.forEach { body["sofort[\($0)]"] = $1 } } + if let wechatPay = wechatPay { + wechatPay.forEach { body["wechat_pay[\($0)]"] = $1 } + } + if let expand = expand { body["expand"] = expand } diff --git a/Sources/StripeKit/Shared Models/StripeModel.swift b/Sources/StripeKit/Shared Models/StripeModel.swift index 019c8cb7..cea790c5 100644 --- a/Sources/StripeKit/Shared Models/StripeModel.swift +++ b/Sources/StripeKit/Shared Models/StripeModel.swift @@ -9,3 +9,4 @@ import Foundation public protocol StripeModel: Codable {} +extension Data: StripeModel {} diff --git a/Sources/StripeKit/StripeClient.swift b/Sources/StripeKit/StripeClient.swift index de0816ae..f67d1b2d 100644 --- a/Sources/StripeKit/StripeClient.swift +++ b/Sources/StripeKit/StripeClient.swift @@ -54,6 +54,8 @@ public final class StripeClient { public var subscriptionSchedules: SubscriptionScheduleRoutes public var taxRates: TaxRateRoutes public var usageRecords: UsageRecordRoutes + public var quoteLineItems: QuoteLineItemRoutes + public var quotes: QuoteRoutes // MARK: - CONNECT public var connectAccounts: AccountRoutes @@ -99,6 +101,10 @@ public final class StripeClient { public var reportRuns: ReportRunRoutes public var reportTypes: ReportTypeRoutes + // MARK: - IDENTITY + public var verificationSessions: VerificationSessionRoutes + public var verificationReports: VerificationReportRoutes + // MARK: - WEBHOOKS public var webhookEndpoints: WebhookEndpointRoutes @@ -152,6 +158,8 @@ public final class StripeClient { subscriptionSchedules = StripeSubscriptionScheduleRoutes(apiHandler: handler) taxRates = StripeTaxRateRoutes(apiHandler: handler) usageRecords = StripeUsageRecordRoutes(apiHandler: handler) + quoteLineItems = StripeQuoteLineItemRoutes(apiHandler: handler) + quotes = StripeQuoteRoutes(apiHandler: handler) connectAccounts = StripeConnectAccountRoutes(apiHandler: handler) accountLinks = StripeAccountLinkRoutes(apiHandler: handler) @@ -190,6 +198,9 @@ public final class StripeClient { reportRuns = StripeReportRunRoutes(apiHandler: handler) reportTypes = StripeReportTypeRoutes(apiHandler: handler) + verificationSessions = StripeVerificationSessionRoutes(apiHandler: handler) + verificationReports = StripeVerificationReportRoutes(apiHandler: handler) + webhookEndpoints = StripeWebhookEndpointRoutes(apiHandler: handler) }