diff --git a/README.md b/README.md index 2d597c6..ded9f0d 100644 --- a/README.md +++ b/README.md @@ -63,64 +63,21 @@ XCTMain([ ## Whats Implemented * [x] Balance Fetching - * [x] History - * [x] Balance by ID * [x] Charges - * [x] Creating Charges - * [x] Retrieving a charge by id - * [x] Listing all charges - * [x] Updating a charge - * [x] Capturing a charge * [x] Customers - * [x] Creating - * [x] Updating - * [x] Deleting - * [x] Fetching by Customer ID - * [x] Listing All Customers (With filters) * [x] Coupons - * [x] Creating - * [x] Updating - * [x] Deleting - * [x] Fetching by Coupon ID - * [x] Listing All Coupons (With filters) * [x] Plans - * [x] Creating - * [x] Updating - * [x] Deleting - * [x] Fetching by Plan ID - * [x] Listing All Plans (With filters) * [x] Refunds - * [x] Creating a Refund - * [x] Retrieval - * [x] Updating - * [x] Listing all * [x] Tokens - * [x] Card Creation - * [x] Bank Creation - * [x] Token Retrieval * [x] Sources - * [x] Creating - * [x] Updating - * [x] Fetching by Source ID -* [x] Subscriptions - * [x] Creating - * [x] Updating - * [x] Deleting - * [x] Fetching by subscription ID - * [x] Listing All Subscriptions (With filters) +* [x] Subscriptions * [x] Connect account - * [x] Creating - * [x] Updating - * [x] Deleting - * [x] Fetching by account ID - * [x] Listing All Accounts (With filters) - * [x] Rejecting accounts - * [x] Creating dashboard login link for express accounts * [x] Orders * [x] Order Items * [x] Products -* [x] Disputes -* [ ] Cards +* [x] Disputes +* [x] Invoices +* [x] Invoice Items [stripe_home]: http://stripe.com "Stripe" [stripe_api]: https://stripe.com/docs/api "Stripe API Endpoints" @@ -129,3 +86,6 @@ XCTMain([ ## License Vapor Stripe Provider is available under the MIT license. See the [LICENSE](LICENSE) file for more info. + +## Want to help? +Feel free to submit a pull request whether it's a clean up, a new approach to handling things, adding a new part of the API, or even if it's just a typo. All help is welcomed! 😀 diff --git a/Sources/Stripe/API/Helpers/Endpoints.swift b/Sources/Stripe/API/Helpers/Endpoints.swift index 57e430f..e9938f8 100644 --- a/Sources/Stripe/API/Helpers/Endpoints.swift +++ b/Sources/Stripe/API/Helpers/Endpoints.swift @@ -94,21 +94,25 @@ internal enum API { /** SOURCES - Source objects allow you to accept a variety of payment methods. They represent a customer's payment instrument and can be used with the Stripe API just like a card object: once chargeable, they can be charged, or attached to customers. + Source objects allow you to accept a variety of payment methods. They represent a customer's payment instrument + and can be used with the Stripe API just like a card object: once chargeable, they can be charged, or attached + to customers. */ case sources case source(String) /** SUBSCRIPTION ITEMS - Subscription items allow you to create customer subscriptions with more than one plan, making it easy to represent complex billing relationships. + Subscription items allow you to create customer subscriptions with more than one plan, making it easy to represent + complex billing relationships. */ case subscriptionItem case subscriptionItems(String) /** SUBSCRIPTIONS - Subscriptions allow you to charge a customer's card on a recurring basis. A subscription ties a customer to a particular plan you've created. + Subscriptions allow you to charge a customer's card on a recurring basis. A subscription ties a customer to a + particular plan you've created. */ case subscription case subscriptions(String) @@ -116,7 +120,8 @@ internal enum API { /** ACCOUNTS - This is an object representing your Stripe account. You can retrieve it to see properties on the account like its current e-mail address or if the account is enabled yet to make live charges. + This is an object representing your Stripe account. You can retrieve it to see properties on the account like its + current e-mail address or if the account is enabled yet to make live charges. */ case account case accounts(String) @@ -161,6 +166,27 @@ internal enum API { case orderReturn case orderReturns(String) + /** + INVOICES + Invoices are statements of what a customer owes for a particular billing period, including subscriptions, + invoice items, and any automatic proration adjustments if necessary. + */ + case invoices + case invoice(String) + case payInvoice(String) + case invoiceLines(String) + case upcomingInvoices + + /** + INVOICE ITEMS + Sometimes you want to add a charge or credit to a customer but only actually charge the customer's card at + the end of a regular billing cycle. This is useful for combining several charges to minimize per-transaction + fees or having Stripe tabulate your usage-based billing totals. + */ + case invoiceItems + case invoiceItem(String) + + var endpoint: String { switch self { case .balance: return APIBase + APIVersion + "balance" @@ -220,6 +246,15 @@ internal enum API { case .orderReturn: return APIBase + APIVersion + "order_returns" case .orderReturns(let id): return APIBase + APIVersion + "order_returns/\(id)" + + case .invoices: return APIBase + APIVersion + "invoices" + case .invoice(let id): return APIBase + APIVersion + "invoices/\(id)" + case .payInvoice(let id): return APIBase + APIVersion + "invoices/\(id)/pay" + case .invoiceLines(let id): return APIBase + APIVersion + "invoices/\(id)/lines" + case .upcomingInvoices: return APIBase + APIVersion + "invoices/upcoming" + + case .invoiceItems:return APIBase + APIVersion + "invoiceitems" + case .invoiceItem(let id): return APIBase + APIVersion + "invoiceitems/\(id)" } } } diff --git a/Sources/Stripe/API/Routes/InvoiceItemRoutes.swift b/Sources/Stripe/API/Routes/InvoiceItemRoutes.swift new file mode 100644 index 0000000..5c84cbd --- /dev/null +++ b/Sources/Stripe/API/Routes/InvoiceItemRoutes.swift @@ -0,0 +1,165 @@ +// +// InvoiceItemRoutes.swift +// Stripe +// +// Created by Anthony Castelli on 9/5/17. +// +// + +import Foundation +import Node +import HTTP + +public final class InvoiceItemRoutes { + + let client: StripeClient + + init(client: StripeClient) { + self.client = client + } + + /** + Create an invoice item + Adds an arbitrary charge or credit to the customer’s upcoming invoice. + + - parameter customer: The ID of the customer who will be billed when this invoice item is billed. + - parameter amount: The integer amount in cents of the charge to be applied to the upcoming invoice. + To apply a credit to the customer’s account, pass a negative amount. + - parameter currency: Three-letter ISO currency code, in lowercase. Must be a supported currency. + - parameter invoice: The ID of an existing invoice to add this invoice item to. When left blank, the + invoice item will be added to the next upcoming scheduled invoice. Use this when adding + invoice items in response to an invoice.created webhook. You cannot add an invoice item + to an invoice that has already been paid, attempted or closed. + - parameter subscription: The ID of a subscription to add this invoice item to. When left blank, the invoice item + will be be added to the next upcoming scheduled invoice. When set, scheduled invoices for + subscriptions other than the specified subscription will ignore the invoice item. Use this + when you want to express that an invoice item has been accrued within the context of a + particular subscription. + - parameter description: An arbitrary string which you can attach to the invoice item. The description is displayed + in the invoice for easy tracking. This will be unset if you POST an empty value. + - parameter discountable: Controls whether discounts apply to this invoice item. Defaults to false for prorations or + negative invoice items, and true for all other invoice items. + - parameter metadata: A set of key/value pairs that you can attach to an invoice item object. It can be useful for + storing additional information about the invoice item in a structured format. You can unset + individual keys if you POST an empty value for that key. You can clear all keys if you POST + an empty value for metadata. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func createItem(forCustomer customer: String, amount: Int, inCurrency currency: StripeCurrency, toInvoice invoice: String? = nil, toSubscription subscription: String? = nil, description: String? = nil, discountable: Bool? = nil, metadata: Node? = nil) throws -> StripeRequest { + var body = Node([:]) + + body["customer"] = Node(customer) + body["amount"] = Node(amount) + body["currency"] = Node(currency.rawValue) + + if let subscription = subscription { + body["subscription"] = Node(subscription) + } + + if let invoice = invoice { + body["invoice"] = Node(invoice) + } + + if let description = description { + body["description"] = Node(description) + } + + if let discountable = discountable { + body["discountable"] = Node(discountable) + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + + return try StripeRequest(client: self.client, method: .post, route: .invoiceItems, body: Body.data(body.formURLEncoded()), headers: nil) + } + + /** + Fetch an invoice + Retrieves the invoice with the given ID. + + - parameter invoice: The ID of the desired invoice item. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func fetch(invoiceItem invoiceItemId: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, method: .post, route: .invoiceItem(invoiceItemId), body: nil, headers: nil) + } + + /** + Update an invoice item + Updates the amount or description of an invoice item on an upcoming invoice. Updating an invoice item + is only possible before the invoice it’s attached to is closed. + + - parameter amount: The integer amount in cents of the charge to be applied to the upcoming invoice. + To apply a credit to the customer’s account, pass a negative amount. + - parameter description: An arbitrary string which you can attach to the invoice item. The description is displayed + in the invoice for easy tracking. This will be unset if you POST an empty value. + - parameter discountable: Controls whether discounts apply to this invoice item. Defaults to false for prorations or + negative invoice items, and true for all other invoice items. + - parameter metadata: A set of key/value pairs that you can attach to an invoice item object. It can be useful for + storing additional information about the invoice item in a structured format. You can unset + individual keys if you POST an empty value for that key. You can clear all keys if you POST + an empty value for metadata. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func update(invoiceItem invoiceItemId: String, amount: Int, description: String? = nil, discountable: Bool? = nil, metadata: Node? = nil) throws -> StripeRequest { + var body = Node([:]) + + body["amount"] = Node(amount) + + if let description = description { + body["description"] = Node(description) + } + + if let discountable = discountable { + body["discountable"] = Node(discountable) + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + + return try StripeRequest(client: self.client, method: .post, route: .invoiceItem(invoiceItemId), body: Body.data(body.formURLEncoded()), headers: nil) + } + + /** + Delete an invoice item + Removes an invoice item from the upcoming invoice. Removing an invoice item is only possible before the + invoice it’s attached to is closed. + + - parameter invoice: The ID of the desired invoice item. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func delete(invoiceItem invoiceItemId: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, method: .delete, route: .invoiceItem(invoiceItemId), body: nil, headers: nil) + } + + /** + List all invoice items + Returns a list of your invoice items. Invoice items are returned sorted by creation date, with the most + recently created invoice items appearing first. + + - parameter filter: A Filter item to pass query parameters when fetching results + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func listAll(customer: String? = nil, filter: StripeFilter? = nil) throws -> StripeRequest { + var query = [String : NodeRepresentable]() + if let customer = customer { + query["customer"] = customer + } + if let data = try filter?.createQuery() { + query = data + } + return try StripeRequest(client: self.client, method: .get, route: .invoiceItems, query: query, body: nil, headers: nil) + } +} diff --git a/Sources/Stripe/API/Routes/InvoiceRoutes.swift b/Sources/Stripe/API/Routes/InvoiceRoutes.swift new file mode 100644 index 0000000..bb590a9 --- /dev/null +++ b/Sources/Stripe/API/Routes/InvoiceRoutes.swift @@ -0,0 +1,275 @@ +// +// InvoiceRoutes.swift +// Stripe +// +// Created by Anthony Castelli on 9/4/17. +// +// + +import Foundation +import Node +import HTTP + +public final class InvoiceRoutes { + + let client: StripeClient + + init(client: StripeClient) { + self.client = client + } + + /** + Create invoice + If you need to invoice your customer outside the regular billing cycle, you can create an invoice that pulls in all + pending invoice items, including prorations. The customer’s billing cycle and regular subscription won’t be affected. + + Once you create the invoice, Stripe will attempt to collect payment according to your subscriptions settings, + though you can choose to pay it right away. + + - parameter customer: The ID of the customer to attach the invoice to + - parameter subscription: The ID of the subscription to invoice. If not set, the created invoice will include all + pending invoice items for the customer. If set, the created invoice will exclude pending + invoice items that pertain to other subscriptions. + - parameter fee: A fee to charge if you are using connected accounts (Must be in cents) + - parameter account: The account to transfer the fee to + - parameter description: A description for the invoice + - parameter metadata: Aditional metadata info + - parameter taxPercent: The percent tax rate applied to the invoice, represented as a decimal number. + - parameter statementDescriptor: Extra information about a charge for the customer’s credit card statement. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + + public func create(forCustomer customer: String, subscription: String? = nil, withFee fee: Int? = nil, toAccount account: String? = nil, description: String? = nil, metadata: Node? = nil, taxPercent: Double? = nil, statementDescriptor: String? = nil) throws -> StripeRequest { + var body = Node([:]) + // Create the headers + var headers: [HeaderKey : String]? + if let account = account { + headers = [ + StripeHeader.Account: account + ] + + if let fee = fee { + body["application_fee"] = Node(fee) + } + } + + body["customer"] = Node(customer) + + if let subscription = subscription { + body["subscription"] = Node(subscription) + } + + if let description = description { + body["description"] = Node(description) + } + + if let taxPercent = taxPercent { + body["tax_percent"] = Node(taxPercent) + } + + if let statementDescriptor = statementDescriptor { + body["statement_descriptor"] = Node(statementDescriptor) + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + + return try StripeRequest(client: self.client, method: .post, route: .invoices, body: Body.data(body.formURLEncoded()), headers: headers) + } + + /** + Fetch an invoice + Retrieves the invoice with the given ID. + + - parameter invoice: The Invoice ID to fetch + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func fetch(invoice invoiceId: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, method: .post, route: .invoice(invoiceId), body: nil, headers: nil) + } + + /** + List items for invoice + When retrieving an invoice, you’ll get a lines property containing the total count of line items and the first handful + of those items. There is also a URL where you can retrieve the full (paginated) list of line items. + + - parameter invoiceId: The Invoice ID to fetch + - parameter customer: In the case of upcoming invoices, the customer of the upcoming invoice is required. In other + cases it is ignored. + - parameter coupon: For upcoming invoices, preview applying this coupon to the invoice. If a subscription or + subscription_items is provided, the invoice returned will preview updating or creating a + subscription with that coupon. Otherwise, it will preview applying that coupon to the customer + for the next upcoming invoice from among the customer’s subscriptions. Otherwise this parameter + is ignored. This will be unset if you POST an empty value. + - parameter filter: Parameters used to filter the results + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + + public func listItems(forInvoice invoiceId: String, customer: String? = nil, coupon: String? = nil, filter: StripeFilter? = nil) throws -> StripeRequest { + var query = [String : NodeRepresentable]() + if let data = try filter?.createQuery() { + query = data + } + + if let customer = customer { + query["customer"] = customer + } + + if let coupon = coupon { + query["coupon"] = coupon + } + + return try StripeRequest(client: self.client, method: .get, route: .invoiceLines(invoiceId), query: query, body: nil, headers: nil) + } + + /** + List Upcoming invoice for Customer + At any time, you can preview the upcoming invoice for a customer. This will show you all the charges that are pending, + including subscription renewal charges, invoice item charges, etc. It will also show you any discount that is applicable + to the customer. + + - parameter customerId: The identifier of the customer whose upcoming invoice you’d like to retrieve. + - parameter coupon: The code of the coupon to apply. If subscription or subscription_items is provided, the invoice + returned will preview updating or creating a subscription with that coupon. Otherwise, it will + preview applying that coupon to the customer for the next upcoming invoice from among the customer’s + subscriptions. The invoice can be previewed without a coupon by passing this value as an empty string. + - parameter subscription: The identifier of the subscription for which you’d like to retrieve the upcoming invoice. If not provided, + but a subscription_items is provided, you will preview creating a subscription with those items. If neither + subscription nor subscription_items is provided, you will retrieve the next upcoming invoice from among the + customer’s subscriptions. + - parameter filter: Parameters used to filter the result + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func upcomingInvoice(forCustomer customerId: String, coupon: String? = nil, subscription: String? = nil, filter: StripeFilter? = nil) throws -> StripeRequest { + var query = [String : NodeRepresentable]() + query["customer"] = customerId + + if let data = try filter?.createQuery() { + query = data + } + + if let coupon = coupon { + query["coupon"] = coupon + } + + if let subscription = subscription { + query["subscription"] = subscription + } + + return try StripeRequest(client: self.client, method: .get, route: .upcomingInvoices, query: query, body: nil, headers: nil) + } + + /** + Update Invoice + Until an invoice is paid, it is marked as open (closed=false). If you’d like to stop Stripe from attempting to collect payment on an + invoice or would simply like to close the invoice out as no longer owed by the customer, you can update the closed parameter. + + - parameter invoiceId: The ID of the Invoice to update + - parameter closed: Boolean representing whether an invoice is closed or not. To close an invoice, pass true. + - parameter forgiven: Boolean representing whether an invoice is forgiven or not. To forgive an invoice, pass true. + Forgiving an invoice instructs us to update the subscription status as if the invoice were successfully paid. + Once an invoice has been forgiven, it cannot be unforgiven or reopened. + - parameter applicationFee: 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. + - parameter account: The account to transfer the fee to + - parameter description: A description for the invoice + - parameter metadata: Aditional metadata info + - parameter taxPercent: The percent tax rate applied to the invoice, represented as a decimal number. The tax rate of an attempted, + paid or forgiven invoice cannot be changed. + - parameter statementDescriptor: Extra information about a charge for the customer’s credit card statement. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func update(invoice invoiceId: String, closed: Bool? = nil, forgiven: Bool? = nil, applicationFee: Int? = nil, toAccount account: String? = nil, description: String? = nil, metadata: Node? = nil, taxPercent: Double? = nil, statementDescriptor: String? = nil) throws -> StripeRequest { + var body = Node([:]) + // Create the headers + var headers: [HeaderKey : String]? + if let account = account { + headers = [ + StripeHeader.Account: account + ] + + if let fee = applicationFee { + body["application_fee"] = Node(fee) + } + } + + if let closed = closed { + body["closed"] = Node(closed) + } + + if let forgiven = forgiven { + body["forgiven"] = Node(forgiven) + } + + if let description = description { + body["description"] = Node(description) + } + + if let taxPercent = taxPercent { + body["tax_percent"] = Node(taxPercent) + } + + if let statementDescriptor = statementDescriptor { + body["statement_descriptor"] = Node(statementDescriptor) + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + + return try StripeRequest(client: self.client, method: .post, route: .invoice(invoiceId), body: Body.data(body.formURLEncoded()), headers: headers) + } + + /** + Pay Invoice + Stripe automatically creates and then attempts to collect payment on invoices for customers on subscriptions according to your + subscriptions settings. However, if you’d like to attempt payment on an invoice out of the normal collection schedule or for some + other reason, you can do so. + + - parameter invoiceId: The ID of the invoice to pay. + - parameter source: A payment source to be charged. The source must be the ID of a source belonging to the customer associated + with the invoice being paid. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func pay(invoice invoiceId: String, source: String? = nil) throws -> StripeRequest { + var body = Node([:]) + + if let source = source { + body["source"] = Node(source) + } + + return try StripeRequest(client: self.client, method: .post, route: .payInvoice(invoiceId), body: Body.data(body.formURLEncoded()), headers: nil) + } + + /** + List all Invoices + You can list all invoices, or list the invoices for a specific customer. The invoices are returned sorted by creation date, + with the most recently created invoices appearing first. + + - parameter filter: A Filter item to pass query parameters when fetching results + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func listAll(customer: String? = nil, filter: StripeFilter? = nil) throws -> StripeRequest { + var query = [String : NodeRepresentable]() + if let customer = customer { + query["customer"] = customer + } + if let data = try filter?.createQuery() { + query = data + } + return try StripeRequest(client: self.client, method: .get, route: .invoices, query: query, body: nil, headers: nil) + } +} diff --git a/Sources/Stripe/API/StripeClient.swift b/Sources/Stripe/API/StripeClient.swift index b2dface..383e180 100644 --- a/Sources/Stripe/API/StripeClient.swift +++ b/Sources/Stripe/API/StripeClient.swift @@ -28,6 +28,8 @@ public class StripeClient { public private(set) var products: ProductRoutes! public private(set) var orders: OrderRoutes! public private(set) var orderReturns: OrderReturnRoutes! + public private(set) var invoices: InvoiceRoutes! + public private(set) var invoiceItems: InvoiceItemRoutes! public init(apiKey: String) throws { self.apiKey = apiKey @@ -50,5 +52,7 @@ public class StripeClient { self.products = ProductRoutes(client: self) self.orders = OrderRoutes(client: self) self.orderReturns = OrderReturnRoutes(client: self) + self.invoices = InvoiceRoutes(client: self) + self.invoiceItems = InvoiceItemRoutes(client: self) } } diff --git a/Sources/Stripe/API/StripeRequest.swift b/Sources/Stripe/API/StripeRequest.swift index 8e8975f..0a82cbc 100644 --- a/Sources/Stripe/API/StripeRequest.swift +++ b/Sources/Stripe/API/StripeRequest.swift @@ -34,7 +34,7 @@ public class StripeRequest { allHeaders[$0.key] = $0.value } } - + switch method { case .get: self.response = try self.httpClient.get(route.endpoint, query: query, allHeaders, body, through: []) case .post: self.response = try self.httpClient.post(route.endpoint, query: query, allHeaders, body, through: []) diff --git a/Sources/Stripe/Models/Invoices/Invoice.swift b/Sources/Stripe/Models/Invoices/Invoice.swift new file mode 100644 index 0000000..2bb49c0 --- /dev/null +++ b/Sources/Stripe/Models/Invoices/Invoice.swift @@ -0,0 +1,160 @@ +// +// Invoice.swift +// Stripe +// +// Created by Anthony Castelli on 9/4/17. +// +// + +import Foundation +import Vapor + +/** + Invoice Model + https://stripe.com/docs/api#invoice_object + */ +public final class Invoice: StripeModelProtocol { + + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amountDue: Int + public private(set) var applicationFee: Int? + public private(set) var attemptCount: Int + public private(set) var hasAttemptedCharge: Bool + public private(set) var charge: String? + public private(set) var isClosed: Bool + public private(set) var customer: String? + public private(set) var date: Date? + public private(set) var description: String? + public private(set) var discount: String? + public private(set) var endingBalance: Int? + public private(set) var isForgiven: Bool + public private(set) var isLiveMode: Bool + public private(set) var isPaid: Bool + + public private(set) var nextPaymentAttempt: Date? + public private(set) var periodStart: Date? + public private(set) var periodEnd: Date? + + public private(set) var receiptNumber: String? + public private(set) var startingBalance: Int? + + public private(set) var statementDescriptor: String? + public private(set) var subscription: String? + + public private(set) var subtotal: Int + public private(set) var total: Int + + public private(set) var tax: Int? + public private(set) var taxPercent: Int? + + public private(set) var webhooksDeliveredAt: Date? + + public private(set) var metadata: Node? + + public private(set) var lines: [InvoiceLineItem]? + public private(set) var currency: StripeCurrency? + + public init(node: Node) throws { + self.id = try node.get("id") + self.object = try node.get("object") + self.amountDue = try node.get("amount_due") + self.applicationFee = try node.get("application_fee") + self.attemptCount = try node.get("attempt_count") + self.hasAttemptedCharge = try node.get("attempted") + self.charge = try node.get("charge") + self.isClosed = try node.get("closed") + self.customer = try node.get("customer") + self.date = try node.get("date") + self.description = try node.get("description") + self.discount = try node.get("discount") + self.endingBalance = try node.get("ending_balance") + self.isForgiven = try node.get("forgiven") + self.isLiveMode = try node.get("livemode") + self.isPaid = try node.get("paid") + self.nextPaymentAttempt = try node.get("next_payment_attempt") + self.periodStart = try node.get("period_start") + self.periodEnd = try node.get("period_end") + self.receiptNumber = try node.get("receipt_number") + self.startingBalance = try node.get("starting_balance") + self.statementDescriptor = try node.get("statement_descriptor") + self.subscription = try node.get("subscription") + self.subtotal = try node.get("subtotal") + self.total = try node.get("total") + self.tax = try node.get("tax") + self.taxPercent = try node.get("tax_percent") + + self.webhooksDeliveredAt = try node.get("webhooks_delivered_at") + + self.metadata = try node.get("metadata") + + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } + + if let lines = node["lines"]?.object, let data = lines["data"]?.array { + self.lines = try data.map({ try InvoiceLineItem(node: $0) }) + } + } + + public func makeNode(in context: Context?) throws -> Node { + let object: [String: Any?] = [ + "id": self.id, + "object": self.object, + "amount_due": self.amountDue, + "application_fee": self.applicationFee, + "attempt_count": self.attemptCount, + "attempted": self.hasAttemptedCharge, + "charge": self.charge, + "closed": self.isClosed, + "customer": self.customer, + "date": self.date, + "description": self.description, + "discount": self.discount, + "ending_balance": self.endingBalance, + "forgiven": self.isForgiven, + "livemode": self.isLiveMode, + "paid": self.isPaid, + "next_payment_attempt": self.nextPaymentAttempt, + "period_start": self.periodStart, + "period_end": self.periodEnd, + "receipt_number": self.receiptNumber, + "starting_balance": self.startingBalance, + "statement_descriptor": self.statementDescriptor, + "subscription": self.subscription, + "subtotal": self.subtotal, + "total": self.total, + "tax": self.tax, + "tax_percent": self.taxPercent, + "webhooks_delivered_at": self.webhooksDeliveredAt, + + "metadata": self.metadata, + + "currency": self.currency?.rawValue + ] + + return try Node(node: object) + } +} + +public final class InvoiceList: StripeModelProtocol { + + public private(set) var object: String? + public private(set) var hasMore: Bool? + public private(set) var items: [Invoice]? + + public init(node: Node) throws { + self.object = try node.get("object") + self.hasMore = try node.get("has_more") + self.items = try node.get("data") + } + + public func makeNode(in context: Context?) throws -> Node { + let object: [String : Any?] = [ + "object": self.object, + "has_more": self.hasMore, + "data": self.items + ] + return try Node(node: object) + } +} diff --git a/Sources/Stripe/Models/Invoices/InvoiceItem.swift b/Sources/Stripe/Models/Invoices/InvoiceItem.swift new file mode 100644 index 0000000..1f61821 --- /dev/null +++ b/Sources/Stripe/Models/Invoices/InvoiceItem.swift @@ -0,0 +1,111 @@ +// +// InvoiceItem.swift +// Stripe +// +// Created by Anthony Castelli on 9/5/17. +// +// + +import Foundation +import Vapor + +/** + Invoice Items + https://stripe.com/docs/api#invoiceitems + */ +public final class InvoiceItem: StripeModelProtocol { + + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amount: Int + public private(set) var customer: String? + public private(set) var date: Date? + public private(set) var description: String? + public private(set) var isDiscountable: Bool + public private(set) var isLiveMode: Bool + public private(set) var isProration: Bool + public private(set) var periodStart: Date? + public private(set) var periodEnd: Date? + public private(set) var quantity: Int? + public private(set) var subscription: String? + + public private(set) var metadata: Node? + + public private(set) var plan: Plan? + public private(set) var currency: StripeCurrency? + + public init(node: Node) throws { + self.id = try node.get("id") + self.object = try node.get("object") + self.amount = try node.get("amount") + self.customer = try node.get("customer") + self.date = try node.get("date") + self.description = try node.get("description") + self.isDiscountable = try node.get("discountable") + self.isLiveMode = try node.get("livemode") + self.isProration = try node.get("proration") + self.quantity = try node.get("qantity") + self.subscription = try node.get("subscription") + + if let period = node["period"]?.object { + self.periodStart = period["start"]?.date + self.periodEnd = period["end"]?.date + } + + self.metadata = try node.get("metadata") + + self.plan = try? node.get("plan") + + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } + } + + public func makeNode(in context: Context?) throws -> Node { + let object: [String: Any?] = [ + "id": self.id, + "object": self.object, + "amount": self.amount, + "customer": self.customer, + "date": self.date, + "description": self.description, + "discountable": self.isDiscountable, + "livemode": self.isLiveMode, + "proration": self.isProration, + "quantity": self.quantity, + "subscription": self.subscription, + "period": [ + "start": self.periodStart, + "end": self.periodEnd + ], + "metadata": self.metadata, + "plan": self.plan, + "currency": self.currency?.rawValue + ] + + return try Node(node: object) + } +} + +public final class InvoiceItemList: StripeModelProtocol { + + public private(set) var object: String? + public private(set) var hasMore: Bool? + public private(set) var items: [InvoiceItem]? + + public init(node: Node) throws { + self.object = try node.get("object") + self.hasMore = try node.get("has_more") + self.items = try node.get("data") + } + + public func makeNode(in context: Context?) throws -> Node { + let object: [String : Any?] = [ + "object": self.object, + "has_more": self.hasMore, + "data": self.items + ] + return try Node(node: object) + } + +} diff --git a/Sources/Stripe/Models/Invoices/InvoiceLineGroup.swift b/Sources/Stripe/Models/Invoices/InvoiceLineGroup.swift new file mode 100644 index 0000000..ae93fde --- /dev/null +++ b/Sources/Stripe/Models/Invoices/InvoiceLineGroup.swift @@ -0,0 +1,32 @@ +// +// InvoiceLineGroup.swift +// Stripe +// +// Created by Anthony Castelli on 9/5/17. +// +// + +import Foundation +import Vapor + +public final class InvoiceLineGroup: StripeModelProtocol { + + public private(set) var object: String? + public private(set) var hasMore: Bool? + public private(set) var items: [InvoiceLineItem]? + + public init(node: Node) throws { + self.object = try node.get("object") + self.hasMore = try node.get("has_more") + self.items = try node.get("data") + } + + public func makeNode(in context: Context?) throws -> Node { + let object: [String : Any?] = [ + "object": self.object, + "has_more": self.hasMore, + "data": self.items + ] + return try Node(node: object) + } +} diff --git a/Sources/Stripe/Models/Invoices/InvoiceLineItem.swift b/Sources/Stripe/Models/Invoices/InvoiceLineItem.swift new file mode 100644 index 0000000..933b044 --- /dev/null +++ b/Sources/Stripe/Models/Invoices/InvoiceLineItem.swift @@ -0,0 +1,82 @@ +// +// InvoiceLineItem.swift +// Stripe +// +// Created by Anthony Castelli on 9/5/17. +// +// + +import Foundation +import Vapor + +public final class InvoiceLineItem: StripeModelProtocol { + + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amount: Int + public private(set) var description: String? + public private(set) var isDiscountable: Bool + public private(set) var isLiveMode: Bool + public private(set) var periodStart: Date? + public private(set) var periodEnd: Date? + public private(set) var isProration: Bool + public private(set) var quantity: Int? + public private(set) var subscription: String? + public private(set) var subscriptionItem: String? + + public private(set) var plan: Plan? + + public private(set) var metadata: Node? + + public private(set) var currency: StripeCurrency? + + public init(node: Node) throws { + self.id = try node.get("id") + self.object = try node.get("object") + self.amount = try node.get("amount") + self.description = try node.get("description") + self.isDiscountable = try node.get("discountable") + self.isLiveMode = try node.get("livemode") + self.isProration = try node.get("proration") + self.quantity = try node.get("quantity") + self.subscription = try node.get("subscription") + self.subscriptionItem = try node.get("subscription_item") + + self.plan = try node.get("plan") + + self.metadata = try node.get("metadata") + + if let period = node["period"]?.object { + self.periodStart = period["start"]?.date + self.periodEnd = period["end"]?.date + } + + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } + } + + public func makeNode(in context: Context?) throws -> Node { + let object: [String: Any?] = [ + "id": self.id, + "object": self.object, + "amount": self.amount, + "description": self.description, + "discountable": self.isDiscountable, + "livemode": self.isLiveMode, + "proration": self.isProration, + "quantity": self.quantity, + "subscription": self.subscription, + "subscription_item": self.subscriptionItem, + "plan": self.plan, + "metadata": self.metadata, + "period": [ + "start": self.periodStart, + "end": self.periodEnd + ], + "currency": self.currency?.rawValue + ] + + return try Node(node: object) + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index dc858f1..511de3d 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 0.7.2 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 0.8.0 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT import XCTest @@ -72,6 +72,27 @@ static var allTests = [ ] } +extension InvoiceItemTests { +static var allTests = [ + ("testCreatingItem", testCreatingItem), + ("testFetchingItem", testFetchingItem), + ("testDeletingItem", testDeletingItem), + ("testUpdateItem", testUpdateItem), + ("testListAllItems", testListAllItems), +] +} + +extension InvoiceTests { +static var allTests = [ + ("testCreatingInvoice", testCreatingInvoice), + ("testFetchingInvoice", testFetchingInvoice), + ("testFetchingInvoiceItems", testFetchingInvoiceItems), + ("testFetchUpcomingInvoice", testFetchUpcomingInvoice), + ("testUpdateInvoice", testUpdateInvoice), + ("testListAllInvoices", testListAllInvoices), +] +} + extension OrderReturnTests { static var allTests = [ ("testRetrieveOrderReturn", testRetrieveOrderReturn), @@ -179,6 +200,8 @@ XCTMain([ testCase(CouponTests.allTests), testCase(CustomerTests.allTests), testCase(DisputeTests.allTests), + testCase(InvoiceItemTests.allTests), + testCase(InvoiceTests.allTests), testCase(OrderReturnTests.allTests), testCase(OrderTests.allTests), testCase(PlanTests.allTests), diff --git a/Tests/StripeTests/InvoiceItemTests.swift b/Tests/StripeTests/InvoiceItemTests.swift new file mode 100644 index 0000000..b78edc0 --- /dev/null +++ b/Tests/StripeTests/InvoiceItemTests.swift @@ -0,0 +1,243 @@ +// +// InvoiceTests.swift +// Stripe +// +// Created by Anthony Castelli on 9/5/17. +// +// + +import XCTest + +@testable import Stripe +@testable import Vapor +@testable import Random + +class InvoiceItemTests: XCTestCase { + + var drop: Droplet? + var customerId = "" + var invoiceItemId = "" + + override func setUp() { + super.setUp() + do { + self.drop = try self.makeDroplet() + + let paymentTokenSource = try self.drop?.stripe?.tokens.createCardToken( + withCardNumber: "4242 4242 4242 4242", + expirationMonth: 10, + expirationYear: 2018, + cvc: 123, + name: "Test Card", + customer: nil, + currency: nil + ).serializedResponse().id ?? "" + + self.customerId = try self.drop?.stripe?.customer.create( + accountBalance: nil, + businessVATId: nil, + coupon: nil, + defaultSource: nil, + description: nil, + email: nil, + shipping: nil, + source: paymentTokenSource + ).serializedResponse().id ?? "" + + self.invoiceItemId = try self.drop?.stripe?.invoiceItems.createItem( + forCustomer: self.customerId, + amount: 10_000, inCurrency: .usd + ).serializedResponse().id ?? "" + + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + fatalError("Setup failed: \(error.localizedDescription)") + } + } + + override func tearDown() { + self.drop = nil + self.customerId = "" + self.invoiceItemId = "" + super.tearDown() + } + + func testCreatingItem() throws { + do { + let object = try self.drop?.stripe?.invoiceItems.createItem( + forCustomer: self.customerId, + amount: 10_000, inCurrency: .usd + ).serializedResponse() + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testFetchingItem() throws { + do { + let object = try self.drop?.stripe?.invoiceItems.fetch(invoiceItem: self.invoiceItemId).serializedResponse() + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testDeletingItem() throws { + do { + let object = try self.drop?.stripe?.invoiceItems.delete(invoiceItem: self.invoiceItemId) + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testUpdateItem() throws { + do { + let object = try self.drop?.stripe?.invoiceItems.update( + invoiceItem: self.invoiceItemId, + amount: 10_000, + description: "Update Invoice" + ).serializedResponse() + + XCTAssertEqual("Update Invoice", object?.description) + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testListAllItems() throws { + do { + let object = try self.drop?.stripe?.invoiceItems.listAll().serializedResponse() + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } +} diff --git a/Tests/StripeTests/InvoiceTests.swift b/Tests/StripeTests/InvoiceTests.swift new file mode 100644 index 0000000..7b076fd --- /dev/null +++ b/Tests/StripeTests/InvoiceTests.swift @@ -0,0 +1,312 @@ +// +// InvoiceTests.swift +// Stripe +// +// Created by Anthony Castelli on 9/5/17. +// +// + +import XCTest + +@testable import Stripe +@testable import Vapor +@testable import Random + +class InvoiceTests: XCTestCase { + + var drop: Droplet? + var customerId = "" + var subscriptionId = "" + var invoiceId = "" + var invoiceItemId = "" + + override func setUp() { + super.setUp() + do { + self.drop = try self.makeDroplet() + + let planId = try self.drop?.stripe?.plans.create( + id: Data(bytes: URandom.bytes(count: 16)).base64String, + amount: 10_00, + currency: .usd, + interval: .week, + name: "Test Plan", + intervalCount: 5, + statementDescriptor: "Test Plan", + trialPeriodDays: nil, + metadata: nil + ).serializedResponse().id ?? "" + + let paymentTokenSource = try self.drop?.stripe?.tokens.createCardToken( + withCardNumber: "4242 4242 4242 4242", + expirationMonth: 10, + expirationYear: 2018, + cvc: 123, + name: "Test Card", + customer: nil, + currency: nil + ).serializedResponse().id ?? "" + + self.customerId = try self.drop?.stripe?.customer.create( + accountBalance: nil, + businessVATId: nil, + coupon: nil, + defaultSource: nil, + description: nil, + email: nil, + shipping: nil, + source: paymentTokenSource + ).serializedResponse().id ?? "" + + self.subscriptionId = try self.drop?.stripe?.subscriptions.create( + forCustomer: self.customerId, + plan: planId, + applicationFeePercent: nil, + couponId: nil, + items: nil, + quantity: nil, + source: nil, + taxPercent: nil, + trialEnd: nil, + trialPeriodDays: nil + ).serializedResponse().id ?? "" + + self.invoiceItemId = try self.drop?.stripe?.invoiceItems.createItem( + forCustomer: self.customerId, + amount: 10_000, inCurrency: .usd + ).serializedResponse().id ?? "" + + self.invoiceId = try drop?.stripe?.invoices.create( + forCustomer: self.customerId, + subscription: self.subscriptionId + ).serializedResponse().id ?? "" + + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + fatalError("Setup failed: \(error.localizedDescription)") + } + } + + override func tearDown() { + self.drop = nil + self.customerId = "" + self.subscriptionId = "" + self.invoiceId = "" + self.invoiceItemId = "" + super.tearDown() + } + + func testCreatingInvoice() throws { + do { + let invoiceItem = try self.drop?.stripe?.invoiceItems.createItem( + forCustomer: self.customerId, + amount: 10_000, inCurrency: .usd + ).serializedResponse().id ?? "" + XCTAssertNotNil(invoiceItem) + + let object = try self.drop?.stripe?.invoices.create( + forCustomer: self.customerId, + subscription: self.subscriptionId + ).serializedResponse() + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testFetchingInvoice() throws { + do { + let object = try self.drop?.stripe?.invoices.fetch(invoice: self.invoiceId).serializedResponse() + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testFetchingInvoiceItems() throws { + do { + let object = try self.drop?.stripe?.invoices.listItems(forInvoice: self.invoiceId) + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testFetchUpcomingInvoice() throws { + do { + let object = try self.drop?.stripe?.invoices.upcomingInvoice(forCustomer: self.customerId).serializedResponse() + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testUpdateInvoice() throws { + do { + let object = try self.drop?.stripe?.invoices.update( + invoice: self.invoiceId, + description: "Update Invoice" + ).serializedResponse() + + XCTAssertEqual("Update Invoice", object?.description) + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } + + func testListAllInvoices() throws { + do { + let object = try self.drop?.stripe?.invoices.listAll().serializedResponse() + XCTAssertNotNil(object) + } catch let error as StripeError { + switch error { + case .apiConnectionError: + XCTFail(error.localizedDescription) + case .apiError: + XCTFail(error.localizedDescription) + case .authenticationError: + XCTFail(error.localizedDescription) + case .cardError: + XCTFail(error.localizedDescription) + case .invalidRequestError: + XCTFail(error.localizedDescription) + case .rateLimitError: + XCTFail(error.localizedDescription) + case .validationError: + XCTFail(error.localizedDescription) + case .invalidSourceType: + XCTFail(error.localizedDescription) + default: + XCTFail(error.localizedDescription) + } + } catch { + XCTFail(error.localizedDescription) + } + } +}