diff --git a/README.md b/README.md index e4abedb..55341f9 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,12 @@ XCTMain([ * [x] Deleting * [x] Fetching by Coupon ID * [x] Listing All Coupons (With filters) -* [ ] Disputes +* [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 @@ -93,6 +98,12 @@ XCTMain([ * [x] Card Creation * [x] Bank Creation * [x] Token Retrieval +* [x] Sources + * [x] Creating + * [x] Updating + * [x] Fetching by Source ID +* [ ] Subscriptions +* [ ] Disputes * [ ] Cards * [ ] Orders * [ ] Order Items diff --git a/Sources/API/Filter/StripeFilter.swift b/Sources/API/Filter/StripeFilter.swift index 9660362..e16c93d 100644 --- a/Sources/API/Filter/StripeFilter.swift +++ b/Sources/API/Filter/StripeFilter.swift @@ -141,7 +141,7 @@ public final class StripeFilter { } if let value = self.source { - node["source"] = value.rawType.makeNode(in: nil) + node["source"] = value.rawValue.makeNode(in: nil) } if let value = self.startingAfter { diff --git a/Sources/API/Helpers/Endpoints.swift b/Sources/API/Helpers/Endpoints.swift index f1c9e53..cf797ee 100644 --- a/Sources/API/Helpers/Endpoints.swift +++ b/Sources/API/Helpers/Endpoints.swift @@ -84,6 +84,20 @@ internal enum API { case coupons case coupon(String) + /** + PLANS + A subscription plan contains the pricing information for different products and feature levels on your site. + */ + case plans + case plan(String) + + /** + 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. + */ + case sources + case source(String) + var endpoint: String { switch self { case .balance: return APIBase + APIVersion + "balance" @@ -106,7 +120,12 @@ internal enum API { case .coupons: return APIBase + APIVersion + "coupons" case .coupon(let id): return APIBase + APIVersion + "coupons/\(id)" + + case .plans: return APIBase + APIVersion + "plans" + case .plan(let id): return APIBase + APIVersion + "plans/\(id)" + + case .sources: return APIBase + APIVersion + "sources" + case .source(let id): return APIBase + APIVersion + "sources/\(id)" } } - } diff --git a/Sources/API/Routes/BalanceRoutes.swift b/Sources/API/Routes/BalanceRoutes.swift index b793d88..13a644b 100644 --- a/Sources/API/Routes/BalanceRoutes.swift +++ b/Sources/API/Routes/BalanceRoutes.swift @@ -32,9 +32,11 @@ public final class BalanceRoutes { Retrieve a balance transaction Retrieves the balance transaction with the given ID. + - parameter transactionId: The ID of the desired balance transaction (as found on any API object that affects the balance, e.g. a charge or transfer). + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func retrieveBalance(forTransaction transactionId: String) throws -> StripeRequest { + public func retrieveBalanceTransaction(_ transactionId: String) throws -> StripeRequest { return try StripeRequest(client: self.client, method: .get, route: .balanceHistoryTransaction(transactionId), query: [:], body: nil, headers: nil) } @@ -47,12 +49,11 @@ public final class BalanceRoutes { - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func history(forFilter filter: StripeFilter?=nil) throws -> StripeRequest { + public func history(forFilter filter: StripeFilter? = nil) throws -> StripeRequest { var query = [String : NodeRepresentable]() if let data = try filter?.createQuery() { query = data } return try StripeRequest(client: self.client, method: .get, route: .balanceHistory, query: query, body: nil, headers: nil) } - } diff --git a/Sources/API/Routes/ChargeRoutes.swift b/Sources/API/Routes/ChargeRoutes.swift index 1c4f70a..11bb915 100644 --- a/Sources/API/Routes/ChargeRoutes.swift +++ b/Sources/API/Routes/ChargeRoutes.swift @@ -32,23 +32,6 @@ public final class ChargeRoutes { self.client = client } - /** - Create a Charge - To charge a credit card, you create a charge object. If your API key is in test mode, the supplied payment source - (e.g., card or Bitcoin receiver) won't actually be charged, though everything else will occur as if in live mode. - (Stripe assumes that the charge would have completed successfully). - - - parameter amount: The payment amount in cents - - parameter currency: The currency in which the charge will be under - - parameter type: The charge type. Either source (card id or token id) or customer (a customer id to charge) - - parameter description: An optional description for charges. - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - public func create(amount: Int, in currency: StripeCurrency, for type: ChargeType, description: String? = nil, capture: Bool = true) throws -> StripeRequest { - return try self.create(amount: amount, in: currency, for: type, withFee: nil, toAccount: nil, description: description, receiptEmail: nil, capture: capture) - } - /** Create a Charge To charge a credit card, you create a charge object. If your API key is in test mode, the supplied payment source @@ -57,42 +40,142 @@ public final class ChargeRoutes { NOTE: Accounts and Fees are only applicable to connected accounts - - parameter amount: The payment amount in cents - - parameter currency: The currency in which the charge will be under - - parameter type: The charge type. Either source (card id or token id) or customer (a customer id to charge) - - parameter fee: A fee to charge if you are using connected accounts (Must be in cents) - - parameter account: The account id which the payment would be sent to. - - parameter description: An optional description for charges. + - parameter amount: The payment amount in cents + + - parameter currency: The currency in which the charge will be under + + - parameter fee: A fee to charge if you are using connected accounts (Must be in cents) + + - parameter account: The account id which the payment would be sent to. + + - parameter capture: Whether or not to immediately capture the charge. + + - parameter description: An optional description for charges. + + - parameter destinationAccountId: If specified, the charge will be attributed to the destination account for tax reporting, + and the funds from the charge will be transferred to the destination account. + + - parameter destinationAmount: The amount to transfer to the destination account without creating an Application Fee. + + - parameter transferGroup: A string that identifies this transaction as part of a group. + + - parameter onBehalfOf: The Stripe account ID that these funds are intended for. + + - parameter receiptEmail: The email address to send this charge's receipt to. + + - parameter shippingLabel: Shipping information for the charge. + + - parameter customer: The ID of an existing customer that will be charged in this request. + + - parameter source: A payment source to be charged, such as a credit card. + + - parameter statementDescriptor: An arbitrary string to be displayed on your customer's credit card statement. + + - parameter metadata: Set of key/value pairs that you can attach to an object. - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func create(amount: Int, in currency: StripeCurrency, for type: ChargeType, withFee fee: Int?, toAccount account: String?, description: String? = nil, receiptEmail: String? = nil, capture: Bool = true) throws -> StripeRequest { + public func create(amount: Int, in currency: StripeCurrency, withFee fee: Int?, toAccount account: String?, capture: Bool?, description: String?, destinationAccountId: String?, destinationAmount: Int?, transferGroup: String?, onBehalfOf: String?, receiptEmail: String?, shippingLabel: ShippingLabel?, customer: String?, statementDescriptor: String?, source: String?, metadata: Node? = nil) throws -> StripeRequest { // Setup our params var body: [String : Any] = [ "amount": amount, "currency": currency.rawValue, - "capture": capture ] - switch type { - case .source(let id): body["source"] = id - case .customer(let id): body["customer"] = id + + // Create the headers + var headers: [HeaderKey : String]? + if let fee = fee, let account = account { + body["application_fee"] = fee + headers = [ + StripeHeader.Account: account + ] + } + + if let capture = capture { + body["capture"] = capture } if let description = description { body["description"] = description } + if let destinationAccountId = destinationAccountId, let destinationAmount = destinationAmount { + body["destination[account]"] = destinationAccountId + body["destination[amount]"] = destinationAmount + } + + if let transferGroup = transferGroup { + body["transfer_group"] = transferGroup + } + + if let onBehalfOf = onBehalfOf { + body["on_behalf_of"] = onBehalfOf + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + if let receiptEmail = receiptEmail { body["receipt_email"] = receiptEmail } - // Create the headers - var headers: [HeaderKey : String]? - if let fee = fee, let account = account { - body["application_fee"] = fee - headers = [ - StripeHeader.Account: account - ] + if let shippingLabel = shippingLabel { + if let name = shippingLabel.name { + body["shipping[name]"] = Node(name) + } + + if let carrier = shippingLabel.carrier { + body["shipping[carrier]"] = Node(carrier) + } + + if let phone = shippingLabel.phone { + body["shipping[phone]"] = Node(phone) + } + + if let trackingNumber = shippingLabel.trackingNumber { + body["shipping[tracking_number]"] = Node(trackingNumber) + } + + if let address = shippingLabel.address { + if let line1 = address.addressLine1 { + body["shipping[address][line1]"] = Node(line1) + } + + if let city = address.city { + body["shipping[address][city]"] = Node(city) + } + + if let country = address.country { + body["shipping[address][country]"] = Node(country) + } + + if let postalCode = address.postalCode { + body["shipping[address][postal_code]"] = Node(postalCode) + } + + if let state = address.state { + body["shipping[address][state]"] = Node(state) + } + + if let line2 = address.addressLine2 { + body["shipping[address][line2]"] = Node(line2) + } + } + } + + if let source = source { + body["source"] = source + } + + if let customer = customer { + body["customer"] = customer + } + + if let statementDescriptor = statementDescriptor { + body["statement_descriptor"] = Node(statementDescriptor) } // Create the body node @@ -101,44 +184,64 @@ public final class ChargeRoutes { return try StripeRequest(client: self.client, method: .post, route: .charges, query: [:], body: Body.data(node.formURLEncoded()), headers: headers) } + + /** + Retrieve a charge + Retrieves the details of a charge that has previously been created. Supply the unique charge ID that was + returned from your previous request, and Stripe will return the corresponding charge information. The same + information is returned when creating or refunding the charge. + + - parameter charge: The chargeId is the ID of the charge + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func retrieve(charge chargeId: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, method: .get, route: .charge(chargeId), query: [:], body: nil, headers: nil) + } + + /** Update a charge - Updates the specified charge by setting the values of the parameters passed. Any parameters not provided will be left - unchanged. This request accepts only the `description`, `metadata`, `receipt_email`, `fraud_details`, and `shipping` as arguments. + Updates the specified charge by setting the values of the parameters passed. Any parameters not provided will + be left unchanged. This request accepts only the `description`, `metadata`, `receipt_email`, `fraud_details`, + and `shipping` as arguments. + + - parameter description: An arbitrary string which you can attach to a charge object. - - parameter chargeId: A charges ID to update + - parameter fraud: A set of key/value pairs you can attach to a charge giving information about its riskiness. - - parameter withFraudReport: One of the FraudReport items (safe, or fraudulent). Note that you must refund a charge before - setting the user_report to fraudulent. Stripe will use the information you send to improve - our fraud detection algorithms. + - parameter receiptEmail: This is the email address that the receipt for this charge will be sent to. - - parameter metadata: An optional dictionary of aditional metadata + - parameter shippingLabel: Shipping information for the charge. Helps prevent fraud on charges for physical goods. - - parameter receiptEmail: This is the email address that the receipt for this charge will be sent to. If this field is - updated, then a new email receipt will be sent to the updated address. This will be unset - if you POST an empty value. + - parameter transferGroup: A string that identifies this transaction as part of a group. - - parameter shippingLabl: Shipping information for the charge. Helps prevent fraud on charges for physical goods. - Create a ShippingLabel() node and pass it here + - parameter metadata: Set of key/value pairs that you can attach to an object. - - parameter transferGroup: A string that identifies this transaction as part of a group. transfer_group may only - be provided if it has not been set. See the Connect documentation for details. + - parameter chargeId: A charges ID to update - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func update(charge chargeId: String, withFraudReport fraud: FraudReport?=nil, description: String?=nil, metadata: [String : String]?=nil, receiptEmail: String?=nil, shippingLabel: ShippingLabel?=nil, transferGroup: String?=nil) throws -> StripeRequest { + public func update(description: String?, fraud: FraudDetails?, receiptEmail: String?, shippingLabel: ShippingLabel?, transferGroup: String?, metadata: Node? = nil, charge chargeId: String) throws -> StripeRequest { var body = Node([:]) - if let fraud = fraud { - body["fraud_details"] = ["user_report": Node(fraud.rawValue)] - } if let description = description { body["description"] = Node(description) } - if let metadata = metadata { + if let fraud = fraud { + if let userReport = fraud.userReport?.rawValue { + body["fraud_details[user_report]"] = Node(userReport) + } + + if let stripeReport = fraud.stripeReport?.rawValue { + body["fraud_details[stripe_report]"] = Node(stripeReport) + } + } + + if let metadata = metadata?.object { for (key, value) in metadata { - body["metadata[\(key)]"] = try Node(node: value) + body["metadata[\(key)]"] = value } } @@ -197,69 +300,34 @@ public final class ChargeRoutes { return try StripeRequest(client: self.client, method: .post, route: .charge(chargeId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) } - /** - Retrieve a charge - Retrieves the details of a charge that has previously been created. Supply the unique charge ID that was - returned from your previous request, and Stripe will return the corresponding charge information. The same - information is returned when creating or refunding the charge. - - - parameter charge: The chargeId is the ID of the charge - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - public func retrieve(charge chargeId: String) throws -> StripeRequest { - return try StripeRequest(client: self.client, method: .get, route: .charge(chargeId), query: [:], body: nil, headers: nil) - } - - /** - List all Charges - Returns a list of charges you’ve previously created. The charges are returned in sorted order, with the most - recent charges 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(filter: StripeFilter?=nil) throws -> StripeRequest { - var query = [String : NodeRepresentable]() - if let data = try filter?.createQuery() { - query = data - } - return try StripeRequest(client: self.client, method: .get, route: .charges, query: query, body: nil, headers: nil) - } - /** Capture a charge - Capture the payment of an existing, uncaptured, charge. This is the second half of the two-step payment flow, - where first you created a charge with the capture option set to false. Uncaptured payments expire exactly - seven days after they are created. If they are not captured by that point in time, they will be marked as + Capture the payment of an existing, uncaptured, charge. This is the second half of the two-step payment flow, + where first you created a charge with the capture option set to false. Uncaptured payments expire exactly + seven days after they are created. If they are not captured by that point in time, they will be marked as refunded and will no longer be capturable. - - parameter chargeId: The chargeId is the ID of the charge + - parameter chargeId: The chargeId is the ID of the charge - - parameter amount: The amount to capture, which must be less than or equal to the original amount. - Any additional amount will be automatically refunded. + - parameter amount: The amount to capture, which must be less than or equal to the original amount. + Any additional amount will be automatically refunded. - - parameter applicationFee: An application fee to add on to this charge. Can only be used with Stripe Connect. + - parameter applicationFee: An application fee to add on to this charge. Can only be used with Stripe Connect. - - parameter destinationAmount: A new destination amount to use. Can only be used with destination charges created - with Stripe Connect.The portion of this charge to send to the destination account. - Must be less than or equal to the captured amount of the charge. + - parameter destinationAmount: A new destination amount to use. Can only be used with destination charges created + with Stripe Connect.The portion of this charge to send to the destination account. + Must be less than or equal to the captured amount of the charge. - - parameter receiptEmail: The email address to send this charge’s receipt to. This will override the - previously-specified email address for this charge, if one was set. Receipts - will not be sent in test mode. + - parameter receiptEmail: The email address to send this charge’s receipt to. This will override the + previously-specified email address for this charge, if one was set. Receipts + will not be sent in test mode. - - parameter statementDescriptor: An arbitrary string to be displayed on your customer’s credit card statement. - This may be up to 22 characters. The statement description may not include <>"' - characters, and will appear on your customer’s statement in capital letters. - Non-ASCII characters are automatically stripped. Updating this value will overwrite - the previous statement descriptor of this charge. While most banks display this - information consistently, some may display it incorrectly or not at all. + - parameter statementDescriptor: An arbitrary string to be displayed on your customer’s credit card statement. + This may be up to 22 characters. - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - public func capture(charge chargeId: String, amount: Int?=nil, applicationFee: Int?=nil, destinationAmount: Int?=nil, receiptEmail: String?=nil, statementDescriptor: String?=nil) throws -> StripeRequest { + */ + public func capture(charge chargeId: String, amount: Int?, applicationFee: Int?, destinationAmount: Int?, receiptEmail: String?, statementDescriptor: String?) throws -> StripeRequest { var body = Node([:]) if let amount = amount { @@ -284,5 +352,23 @@ public final class ChargeRoutes { return try StripeRequest(client: self.client, method: .post, route: .captureCharge(chargeId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) } - + + /** + List all Charges + Returns a list of charges you’ve previously created. The charges are returned in sorted order, with the most + recent charges 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(filter: StripeFilter?) throws -> StripeRequest { + var query = [String : NodeRepresentable]() + if let data = try filter?.createQuery() { + query = data + } + return try StripeRequest(client: self.client, method: .get, route: .charges, query: query, body: nil, headers: nil) + } + + } diff --git a/Sources/API/Routes/CouponRoutes.swift b/Sources/API/Routes/CouponRoutes.swift index 4493d6a..060db8c 100644 --- a/Sources/API/Routes/CouponRoutes.swift +++ b/Sources/API/Routes/CouponRoutes.swift @@ -24,34 +24,34 @@ public final class CouponRoutes { Create a coupon Creates a new coupon object. - - parameter id: Unique string of your choice that will be used to identify this coupon when applying it to a customer. - This is often a specific code you’ll give to your customer to use when signing up (e.g. FALL25OFF). If - you don’t want to specify a particular code, you can leave the ID blank and Stripe will generate a - random code for you. + - parameter id: Unique string of your choice that will be used to identify this coupon when applying it to a customer. + This is often a specific code you’ll give to your customer to use when signing up (e.g. FALL25OFF). If + you don’t want to specify a particular code, you can leave the ID blank and Stripe will generate a + random code for you. - - parameter duration: Specifies how long the discount will be in effect. + - parameter duration: Specifies how long the discount will be in effect. - - parameter amountOff: Amount to subtract from an invoice total (required if percent_off is not passed). + - parameter amountOff: Amount to subtract from an invoice total (required if percent_off is not passed). - - parameter currency: The currency in which the charge will be under. (required if amount_off passed). + - parameter currency: The currency in which the charge will be under. (required if amount_off passed). - - parameter durationInMonths: Required only if duration is `repeating`, in which case it must be a positive - integer that specifies the number of months the discount will be in effect. + - parameter durationInMonths: Required only if duration is `repeating`, in which case it must be a positive + integer that specifies the number of months the discount will be in effect. - - parameter maxRedemptions: A positive integer specifying the number of times the coupon can be redeemed before it’s - no longer valid. + - parameter maxRedemptions: A positive integer specifying the number of times the coupon can be redeemed before it’s + no longer valid. - - parameter percentOff: A positive integer between 1 and 100 that represents the discount the coupon will apply - (required if amount_off is not passed). + - parameter percentOff: A positive integer between 1 and 100 that represents the discount the coupon will apply + (required if amount_off is not passed). - - parameter redeemBy: Unix timestamp specifying the last time at which the coupon can be redeemed. + - parameter redeemBy: Unix timestamp specifying the last time at which the coupon can be redeemed. - - parameter metaData: A set of key/value pairs that you can attach to a coupon object. + - parameter metaData: A set of key/value pairs that you can attach to a coupon object. - returns: A StripeRequest<> item which you can then use to convert to the corresponding node. */ - public func create(id: String?, duration: StripeDuration, amountOff: Int?, currency: StripeCurrency?, durationInMonths: Int?, maxRedemptions: Int?, percentOff: Int?, redeemBy: Date?, metadata: [String: String]?) throws -> StripeRequest { + public func create(id: String?, duration: StripeDuration, amountOff: Int?, currency: StripeCurrency?, durationInMonths: Int?, maxRedemptions: Int?, percentOff: Int?, redeemBy: Date?, metadata: Node? = nil) throws -> StripeRequest { var body = Node([:]) body["duration"] = Node(duration.rawValue) @@ -80,55 +80,7 @@ public final class CouponRoutes { body["redeem_by"] = Node(Int(redeemBy.timeIntervalSince1970)) } - if let metadata = metadata { - for (key, value) in metadata { - body["metadata[\(key)]"] = try Node(node: value) - } - } - - return try StripeRequest(client: self.client, method: .post, route: .coupons, query: [:], body: Body.data(body.formURLEncoded()), headers: nil) - } - - /** - Create a coupon - Creates a new coupon object. - - - parameter coupon: A coupon object created with appropriate values set. Any unset parameters (nil) - will unset the value on stripe. - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node. - */ - - public func create(coupon: Coupon) throws -> StripeRequest { - var body = Node([:]) - - body["duration"] = Node(coupon.duration.rawValue) - - if let amountOff = coupon.amountOff { - body["amount_off"] = Node(amountOff) - } - - if let currency = coupon.currency { - body["currency"] = Node(currency.rawValue) - } - - if let durationInMonths = coupon.durationInMonths { - body["duration_in_months"] = Node(durationInMonths) - } - - if let maxRedemptions = coupon.maxRedemptions { - body["max_redemptions"] = Node(maxRedemptions) - } - - if let percentOff = coupon.percentOff { - body["percent_off"] = Node(percentOff) - } - - if let redeemBy = coupon.redeemBy { - body["redeem_by"] = Node(redeemBy.timeIntervalSince1970) - } - - if let metadata = coupon.metadata?.object { + if let metadata = metadata?.object { for (key, value) in metadata { body["metadata[\(key)]"] = value } @@ -157,19 +109,19 @@ public final class CouponRoutes { - parameter couponId: The identifier of the coupon to be updated. - parameter metaData: A set of key/value pairs that you can attach to a coupon object. It can be useful for storing additional - information about the coupon in a structured format. + information about the coupon in a structured format (Non optional since it's the only mutable property) - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func update(metadata: [String:String], forCouponId couponId: String) throws -> StripeRequest { + public func update(metadata: Node, forCouponId couponId: String) throws -> StripeRequest { var body = Node([:]) - for (key, value) in metadata - { - body["metadata[\(key)]"] = try Node(node: "\(String(describing: value))") + if let metadata = metadata.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } } - return try StripeRequest(client: self.client, method: .post, route: .coupon(couponId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) } @@ -196,7 +148,7 @@ public final class CouponRoutes { - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func listAll(filter: StripeFilter?=nil) throws -> StripeRequest { + public func listAll(filter: StripeFilter?) throws -> StripeRequest { var query = [String : NodeRepresentable]() if let data = try filter?.createQuery() diff --git a/Sources/API/Routes/CustomerRoutes.swift b/Sources/API/Routes/CustomerRoutes.swift index 450a5b6..43e0bdd 100644 --- a/Sources/API/Routes/CustomerRoutes.swift +++ b/Sources/API/Routes/CustomerRoutes.swift @@ -25,159 +25,141 @@ public final class CustomerRoutes { Create a customer Creates a new customer object. + - parameter accountBalance: An integer amount in cents that is the starting account balance for your customer. + A negative amount represents a credit that will be used before attempting any charges + to the customer’s card; a positive amount will be added to the next invoice. + + - parameter businessVATId: The customer’s VAT identification number. + + - parameter coupon: If you provide a coupon code, the customer will have a discount applied on all recurring charges. + + - parameter defaultSource: Either a card + + - parameter description: An arbitrary string that you can attach to a customer object. It is displayed + alongside the customer in the dashboard. This will be unset if you POST an + empty value. + - parameter email: The Customer’s email address. It’s displayed alongside the customer in your dashboard and can be useful for searching and tracking. - - parameter description: An arbitrary string that you can attach to a customer object. It is displayed - alongside the customer in the dashboard. This will be unset if you POST an - empty value. + - parameter shippingLabel: Shipping label. + + - parameter source: A one time token ID created from a source. - - parameter metadata: A set of key/value pairs that you can attach to a customer object. It can be - useful for storing additional information about the customer 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. + - parameter metadata: A set of key/value pairs that you can attach to a customer object. It can be + useful for storing additional information about the customer 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 create(email: String, currency: StripeCurrency? = nil, description: String? = nil, metadata: [String : String]? = nil) throws -> StripeRequest { + public func create(accountBalance: Int?, businessVATId: String?, coupon: String?, defaultSource: String?, description: String?, email: String?, shipping: ShippingLabel?, source: String?, metadata: Node? = nil) throws -> StripeRequest { var body = Node([:]) - body["email"] = Node(email) - - if let currency = currency { - body["currency"] = Node(currency.rawValue) + if let accountBalance = accountBalance { + body["account_balance"] = Node(accountBalance) } - if let description = description { - body["description"] = Node(description) + if let businessVATId = businessVATId { + body["business_vat_id"] = Node(businessVATId) } - if let metadata = metadata { - for (key, value) in metadata { - body["metadata[\(key)]"] = try Node(node: value) - } + if let coupon = coupon { + body["coupon"] = Node(coupon) } - return try StripeRequest(client: self.client, method: .post, route: .customers, query: [:], body: Body.data(body.formURLEncoded()), headers: nil) - } - - /** - Create a customer - Creates a new customer object. - - - parameter customer: A customer class created with appropiate values set. Any unset parameters (nil) - will unset the value on stripe - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - public func create(customer: Customer) throws -> StripeRequest { - var body = Node([:]) - - if let email = customer.email { - body["email"] = Node(email) + if let defaultSource = defaultSource { + body["default_source"] = Node(defaultSource) } - if let description = customer.description { + if let description = description { body["description"] = Node(description) } - if let bussinessVATId = customer.bussinessVATId { - body["business_vat_id"] = Node(bussinessVATId) - } - - if let defaultSourceId = customer.defaultSourceId { - body["source"] = Node(defaultSourceId) - } - - if let currency = customer.currency { - body["currency"] = Node(currency.rawValue) + if let email = email { + body["email"] = Node(email) } - if let metadata = customer.metadata?.object { + if let metadata = metadata?.object { for (key, value) in metadata { body["metadata[\(key)]"] = value } } - return try StripeRequest(client: self.client, method: .post, route: .customers, query: [:], body: Body.data(body.formURLEncoded()), headers: nil) - } - - /** - Creats a source for the customer - - - parameter customer: The customer object to add the source to - - parameter account: A connect account to add the customer to - - parameter source: The source token to add to the customer. - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - - public func createSource(for customer: Customer, inAccount account: String?=nil, source: String) throws -> StripeRequest { - let body = try Node(node: ["source": source]) - - var headers: [HeaderKey: String]? + if let shippingLabel = shipping { + if let name = shippingLabel.name { + body["shipping[name]"] = Node(name) + } + + if let carrier = shippingLabel.carrier { + body["shipping[carrier]"] = Node(carrier) + } + + if let phone = shippingLabel.phone { + body["shipping[phone]"] = Node(phone) + } + + if let trackingNumber = shippingLabel.trackingNumber { + body["shipping[tracking_number]"] = Node(trackingNumber) + } + + if let address = shippingLabel.address { + if let line1 = address.addressLine1 { + body["shipping[address][line1]"] = Node(line1) + } + + if let city = address.city { + body["shipping[address][city]"] = Node(city) + } + + if let country = address.country { + body["shipping[address][country]"] = Node(country) + } + + if let postalCode = address.postalCode { + body["shipping[address][postal_code]"] = Node(postalCode) + } + + if let state = address.state { + body["shipping[address][state]"] = Node(state) + } + + if let line2 = address.addressLine2 { + body["shipping[address][line2]"] = Node(line2) + } + } + } - // Check if we have an account to set it to - if let account = account { - headers = ["Stripe-Account": account] + if let source = source { + body["source"] = Node(source) } - return try StripeRequest(client: self.client, method: .post, route: .customerSources(customer.id), query: [:], body: Body.data(body.formURLEncoded()), headers: headers) + return try StripeRequest(client: self.client, method: .post, route: .customers, query: [:], body: Body.data(body.formURLEncoded()), headers: nil) } - + /** - Creats a source for the customer + Adds a new source for the customer. Either a bank account token or a card token. - parameter customerId: The customer object to add the source to - parameter account: A connect account to add the customer to - - parameter source: The source token to add to the customer. + - parameter source: The source token to add to the customer (Bank account or Card). - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func createSource(for customerId: String, inAccount account: String?=nil, source: String) throws -> StripeRequest { + public func addNewSource(for customerId: String, inConnectAccount account: String?, source: String) throws -> StripeRequest { let body = try Node(node: ["source": source]) var headers: [HeaderKey: String]? // Check if we have an account to set it to if let account = account { - headers = ["Stripe-Account": account] + headers = [StripeHeader.Account : account] } return try StripeRequest(client: self.client, method: .post, route: .customerSources(customerId), query: [:], body: Body.data(body.formURLEncoded()), headers: headers) } - /** - Updates or sets the default source for the customer - - - parameter customer: The customer object to add the source to - - parameter account: A connect account to add the customer to - - parameter source: The source token to add to the customer. - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - - public func updateDefaultSource(for customer: Customer, source: String) throws -> StripeRequest { - let body = try Node(node: ["default_source": source]) - return try StripeRequest(client: self.client, method: .post, route: .customer(customer.id), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) - } - - /** - Updates or sets the default source for the customer - - - parameter customerId: The customer object to add the source to - - parameter account: A connect account to add the customer to - - parameter source: The source token to add to the customer. - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - - public func updateDefaultSource(for customerId: String, source: String) throws -> StripeRequest { - let body = try Node(node: ["default_source": source]) - return try StripeRequest(client: self.client, method: .post, route: .customer(customerId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) - } - /** Retrieve a customer Retrieves the details of an existing customer. You need only supply the unique customer identifier @@ -205,34 +187,117 @@ public final class CustomerRoutes { This request accepts mostly the same arguments as the customer creation call. - - parameter customer: A customer class created with appropiate values set. Any unset parameters (nil) - will unset the value on stripe + - parameter accountBalance: An integer amount in cents that is the starting account balance for your customer. + A negative amount represents a credit that will be used before attempting any charges + to the customer’s card; a positive amount will be added to the next invoice. + + - parameter businessVATId: The customer’s VAT identification number. + + - parameter coupon: If you provide a coupon code, the customer will have a discount applied on all recurring charges. + + - parameter defaultSourceId: Either a card + + - parameter description: An arbitrary string that you can attach to a customer object. It is displayed + alongside the customer in the dashboard. This will be unset if you POST an + empty value. + + - parameter email: The Customer’s email address. It’s displayed alongside the customer in your + dashboard and can be useful for searching and tracking. + + + - parameter shippingLabel: Shipping label. + + - parameter newSource: A one time token ID created from a source. + + - parameter metadata: A set of key/value pairs that you can attach to a customer object. It can be + useful for storing additional information about the customer 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. + + - parameter customerId: A customer class created with appropiate values set. Any unset parameters (nil) + will unset the value on stripe - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func update(customer: Customer, forCustomerId customerId: String) throws -> StripeRequest { + public func update(accountBalance: Int?, businessVATId: String?, coupon: String?, defaultSourceId: String?, description:String?, email: String?, shipping:ShippingLabel?, newSource: String?, metadata: Node? = nil, forCustomerId customerId: String) throws -> StripeRequest { var body = Node([:]) - if let email = customer.email { - body["email"] = Node(email) + if let accountBalance = accountBalance { + body["account_balance"] = Node(accountBalance) } - if let description = customer.description { - body["description"] = Node(description) + if let businessVATId = businessVATId { + body["business_vat_id"] = Node(businessVATId) + } + + if let coupon = coupon { + body["coupon"] = Node(coupon) } - if let bussinessVATId = customer.bussinessVATId { - body["business_vat_id"] = Node(bussinessVATId) + if let defaultSourceId = defaultSourceId { + body["default_source"] = Node(defaultSourceId) } - if let defaultSourceId = customer.defaultSourceId { - body["source"] = Node(defaultSourceId) + if let description = description { + body["description"] = Node(description) } - if let metadata = customer.metadata?.object { + if let email = email { + body["email"] = Node(email) + } + + if let metadata = metadata?.object { for (key, value) in metadata { - body["metadata[\(key)]"] = try Node(node: "\(String(describing: value))") + body["metadata[\(key)]"] = value + } + } + + if let shippingLabel = shipping { + if let name = shippingLabel.name { + body["shipping[name]"] = Node(name) } + + if let carrier = shippingLabel.carrier { + body["shipping[carrier]"] = Node(carrier) + } + + if let phone = shippingLabel.phone { + body["shipping[phone]"] = Node(phone) + } + + if let trackingNumber = shippingLabel.trackingNumber { + body["shipping[tracking_number]"] = Node(trackingNumber) + } + + if let address = shippingLabel.address { + if let line1 = address.addressLine1 { + body["shipping[address][line1]"] = Node(line1) + } + + if let city = address.city { + body["shipping[address][city]"] = Node(city) + } + + if let country = address.country { + body["shipping[address][country]"] = Node(country) + } + + if let postalCode = address.postalCode { + body["shipping[address][postal_code]"] = Node(postalCode) + } + + if let state = address.state { + body["shipping[address][state]"] = Node(state) + } + + if let line2 = address.addressLine2 { + body["shipping[address][line2]"] = Node(line2) + } + } + } + + if let newSource = newSource { + body["source"] = Node(newSource) } return try StripeRequest(client: self.client, method: .post, route: .customer(customerId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) @@ -259,7 +324,7 @@ public final class CustomerRoutes { - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func listAll(filter: StripeFilter?=nil) throws -> StripeRequest { + public func listAll(filter: StripeFilter?) throws -> StripeRequest { var query = [String : NodeRepresentable]() if let data = try filter?.createQuery() { query = data diff --git a/Sources/API/Routes/PlanRoutes.swift b/Sources/API/Routes/PlanRoutes.swift new file mode 100644 index 0000000..5344cab --- /dev/null +++ b/Sources/API/Routes/PlanRoutes.swift @@ -0,0 +1,170 @@ +// +// PlanRoutes.swift +// Stripe +// +// Created by Andrew Edwards on 5/29/17. +// +// + +import Foundation +import Node +import HTTP +import Models +import Helpers +import Errors + +public final class PlanRoutes { + let client: StripeClient + + init(client: StripeClient) { + self.client = client + } + + /** + Create a plan + Creates a new plan object. + + - parameter id: Unique string of your choice that will be used to identify this plan when subscribing a customer. + This could be an identifier like “gold” or a primary key from your own database. + + - parameter amount: A positive integer in cents (or 0 for a free plan) representing how much to charge (on a recurring basis). + + - parameter currency: The currency in which the charge will be under. (required if amount_off passed). + + - parameter interval: Specifies billing frequency. Either day, week, month or year. + + - parameter name: Name of the plan, to be displayed on invoices and in the web interface. + + - parameter intervalCount: The number of intervals between each subscription billing. + + - parameter statementDescriptor: An arbitrary string to be displayed on your customer’s credit card statement. + This may be up to 22 characters. + + - parameter trialPeriodDays: Specifies a trial period in (an integer number of) days. + + - parameter metaData: A set of key/value pairs that you can attach to a plan object. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node. + */ + + public func create(id: String, amount: Int, currency: StripeCurrency, interval: StripeInterval, name: String, intervalCount: Int?, statementDescriptor: String?, trialPeriodDays: Int?, metadata: Node? = nil) throws -> StripeRequest { + var body = Node([:]) + + body["id"] = Node(id) + + body["amount"] = Node(amount) + + body["currency"] = Node(currency.rawValue) + + body["interval"] = Node(interval.rawValue) + + body["name"] = Node(name) + + if let intervalCount = intervalCount { + body["interval_count"] = Node(intervalCount) + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + + if let statementDescriptor = statementDescriptor { + body["statement_descriptor"] = Node(statementDescriptor) + } + + if let trialPeriodDays = trialPeriodDays { + body["trial_period_days"] = Node(trialPeriodDays) + } + + return try StripeRequest(client: self.client, method: .post, route: .plans, query: [:], body: Body.data(body.formURLEncoded()), headers: nil) + } + + /** + Retrieve a plan + Retrieves the plan with the given ID. + + - parameter planId: The ID of the desired plan. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + + public func retrieve(plan planId: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, method: .get, route: .plan(planId), query: [:], body: nil, headers: nil) + } + + /** + Update a plan + Updates the name or other attributes of a plan. Other plan details (price, interval, etc.) are, by design, not editable. + + - parameter name: Name of the plan, to be displayed on invoices and in the web interface. + + - parameter statementDescriptor: An arbitrary string to be displayed on your customer’s credit card statement. + + - parameter trialPeriodDays: Specifies a trial period in (an integer number of) days. + + - parameter metaData: A set of key/value pairs that you can attach to a plan object. + + - parameter planId: The identifier of the plan to be updated. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + + public func update(name: String?, statementDescriptor: String?, trialPeriodDays: Int?, metadata: Node? = nil, forPlanId planId: String) throws -> StripeRequest { + var body = Node([:]) + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + + if let name = name { + body["name"] = Node(name) + } + + if let statementDescriptor = statementDescriptor { + body["statement_descriptor"] = Node(statementDescriptor) + } + + if let trialPeriodDays = trialPeriodDays { + body["trial_period_days"] = Node(trialPeriodDays) + } + + return try StripeRequest(client: self.client, method: .post, route: .plan(planId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) + } + + /** + Delete a plan + Deleting a plan does not affect any current subscribers to the plan; it merely means that new subscribers can’t be added to that plan. + + - parameter couponId: The identifier of the plan to be deleted. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + + public func delete(plan planId: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, method: .delete, route: .plan(planId), query: [:], body: nil, headers: nil) + } + + /** + List all plans + Returns a list of your plans. The plans are returned sorted by creation date, with the + most recent plans 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(filter: StripeFilter?) throws -> StripeRequest { + var query = [String : NodeRepresentable]() + + if let data = try filter?.createQuery() { + query = data + } + + return try StripeRequest(client: self.client, method: .get, route: .plans, query: query, body: nil, headers: nil) + } +} diff --git a/Sources/API/Routes/RefundRoutes.swift b/Sources/API/Routes/RefundRoutes.swift index a31f196..826c56b 100644 --- a/Sources/API/Routes/RefundRoutes.swift +++ b/Sources/API/Routes/RefundRoutes.swift @@ -37,7 +37,7 @@ public final class RefundRoutes { - parameter amount: A positive integer in cents representing how much of this charge to refund. Can only refund up to the unrefunded amount remaining of the charge. - - parameter reason: String indicating the reason for the refund. If set, possible values + - parameter reason: String indicating the reason for the refund. If set, possible values are duplicate, fraudulent, and requestedByCustomer. Specifying fraudulent as the reason when you believe the charge to be fraudulent will help us improve our fraud detection algorithms. @@ -54,14 +54,15 @@ public final class RefundRoutes { (either the entire or partial amount). A transfer can only be reversed by the application that created the charge. - - parameter metadata: A set of key/value pairs that you can attach to a refund object. It can be - useful for storing additional information about the refund 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. + - parameter metadata: A set of key/value pairs that you can attach to a refund object. It can be + useful for storing additional information about the refund 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 refund(charge: String, amount: Int? = nil, reason: RefundReason? = nil, refundApplicationFee: Bool? = nil, reverseTransfer: Bool? = nil, metadata: [String : String]? = nil) throws -> StripeRequest { + public func createRefund(charge: String, amount: Int?, reason: RefundReason?, refundApplicationFee: Bool?, reverseTransfer: Bool?, metadata: Node? = nil) throws -> StripeRequest { var body = Node([:]) body["charge"] = Node(charge) @@ -70,6 +71,12 @@ public final class RefundRoutes { body["amount"] = Node(amount) } + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + if let reason = reason { body["reason"] = Node(reason.rawValue) } @@ -82,15 +89,8 @@ public final class RefundRoutes { body["reverse_transfer"] = Node(reverseTransfer) } - if let metadata = metadata { - for (key, value) in metadata { - body["metadata[\(key)]"] = try Node(node: value) - } - } - return try StripeRequest(client: self.client, method: .post, route: .refunds, body: Body.data(body.formURLEncoded())) } - /** Retrieve a refund @@ -109,20 +109,20 @@ public final class RefundRoutes { Updates the specified refund by setting the values of the parameters passed. Any parameters not provided will be left unchanged. - - parameter refund: The ID of the refund to update. - - parameter metadata: A set of key/value pairs that you can attach to a refund object. It can be useful for storing additional information about the refund 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. + - parameter refund: The ID of the refund to update. + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func update(refund refundId: String, metadata: [String : String]? = nil) throws -> StripeRequest { + public func update(metadata: Node? = nil, refund refundId: String) throws -> StripeRequest { var body = Node([:]) - if let metadata = metadata { + if let metadata = metadata?.object { for (key, value) in metadata { - body["metadata[\(key)]"] = try Node(node: value) + body["metadata[\(key)]"] = value } } return try StripeRequest(client: self.client, method: .post, route: .refund(refundId), body: Body.data(body.formURLEncoded())) @@ -133,15 +133,23 @@ public final class RefundRoutes { recent refunds appearing first. For convenience, the 10 most recent refunds are always available by default on the charge object. - - parameter filter: A Filter item to pass query parameters when fetching results + - parameter byChargeId: Only return refunds for the charge specified by this charge ID. + + - 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(by charge: String? = nil, filter: StripeFilter?=nil) throws -> StripeRequest { + public func listAll(byChargeId charge: String?, filter: StripeFilter?) throws -> StripeRequest { var query = [String : NodeRepresentable]() + if let data = try filter?.createQuery() { query = data } + + if let charge = charge { + query["charge"] = Node(charge) + } + return try StripeRequest(client: self.client, method: .get, route: .refunds, query: query) } } diff --git a/Sources/API/Routes/SourceRoutes.swift b/Sources/API/Routes/SourceRoutes.swift new file mode 100644 index 0000000..f27f14d --- /dev/null +++ b/Sources/API/Routes/SourceRoutes.swift @@ -0,0 +1,268 @@ +// +// SourceRoutes.swift +// Stripe +// +// Created by Andrew Edwards on 6/1/17. +// +// + +import Foundation +import Node +import HTTP +import Models +import Helpers +import Errors + +public final class SourceRoutes { + + let client: StripeClient + + init(client: StripeClient) { + self.client = client + } + + /** + Create a Source + Creates a new customer object. + + - parameter type: The type of the source to create. + + - parameter amount: Amount associated with the source. This is the amount for which the source + will be chargeable once ready. + + - parameter currency: This is the currency for which the source will be chargeable once ready. + + - parameter flow: The authentication flow of the source to create. + + - parameter owner: Information about the owner of the payment instrument that may be used or + required by particular source types. + + - parameter redirectReturnUrl: The URL you provide to redirect the customer back to you after they + authenticated their payment. + + - parameter token: An optional token used to create the source. When passed, token properties + will override source parameters. + + - parameter usage: Either reusable or single_use. + + - parameter metadata: A set of key/value pairs that you can attach to a customer object. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + + public func createNewSource(sourceType: SourceType, source: [String:Node]?, amount: Int?, currency: StripeCurrency?, flow: String?, owner: Owner?, redirectReturnUrl: String?, token: String?, usage: String?, metadata: Node? = nil) throws -> StripeRequest { + + var body = Node([:]) + + body["type"] = Node(sourceType.rawValue) + + switch sourceType{ + + case .card: + if let source = source { + for (key,val) in source { + body["card[\(key)]"] = val + } + } + case .bitcoin: + if let source = source { + for (key,val) in source { + body["bitcoin[\(key)]"] = val + } + } + case .threeDSecure: + if let source = source { + for (key,val) in source { + body["three_d_secure[\(key)]"] = val + } + } + + case .bancontact: + if let source = source { + for (key,val) in source { + body["bancontact[\(key)]"] = val + } + } + case .giropay: + if let source = source { + for (key,val) in source { + body["giropay[\(key)]"] = val + } + } + case .ideal: + if let source = source { + for (key,val) in source { + body["ideal[\(key)]"] = val + } + } + case .sepaDebit: + if let source = source { + for (key,val) in source { + body["sepa_debit[\(key)]"] = val + } + } + case .sofort: + if let source = source { + for (key,val) in source { + body["sofort[\(key)]"] = val + } + } + default: + body[""] = "" + } + + if let amount = amount { + + body["amount"] = Node(amount) + } + + if let currency = currency { + body["currency"] = Node(currency.rawValue) + } + + if let flow = flow { + body["flow"] = Node(flow) + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + + if let owner = owner { + + if let email = owner.email { + body["owner[email]"] = Node(email) + } + + if let name = owner.name { + body["owner[name]"] = Node(name) + } + + if let phone = owner.phone { + body["owner[phone]"] = Node(phone) + } + + if let address = owner.address { + + if let line1 = address.addressLine1 { + body["owner[address][line1]"] = Node(line1) + } + + if let city = address.city { + body["owner[address][city]"] = Node(city) + } + + if let country = address.country { + body["owner[address][country]"] = Node(country) + } + + if let postalCode = address.postalCode { + body["owner[address][postal_code]"] = Node(postalCode) + } + + if let state = address.state { + body["owner[address][state]"] = Node(state) + } + + if let line2 = address.addressLine2 { + body["owner[address][line2]"] = Node(line2) + } + } + } + + if let redirectReturnUrl = redirectReturnUrl { + body["redirect[return_url]"] = Node(redirectReturnUrl) + } + + if let token = token { + body["token"] = Node(token) + } + + if let usage = usage { + body["usage"] = Node(usage) + } + + return try StripeRequest(client: self.client, method: .post, route: .sources, query: [:], body: Body.data(body.formURLEncoded()), headers: nil) + } + + /** + Retrieve a source + Retrieves an existing source object. Supply the unique source ID from a source creation request and Stripe will return the corresponding up-to-date source object information. + + - parameter sourceId: The identifier of the source to be retrieved. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func retrieveSource(withId sourceId: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, method: .get, route: .source(sourceId), query: [:], body: nil, headers: nil) + } + + /** + Update a Source + Updates the specified source by setting the values of the parameters passed. Any parameters not provided will be left unchanged. + + - parameter metadata: A set of key/value pairs that you can attach to a source object. + + - parameter owner: Information about the owner of the payment instrument that may be used or required by particular + source types. + + - parameter sourceId: The identifier of the source to be updated. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node. + */ + + public func update(owner: Owner?, metadata: Node? = nil, forSourceId sourceId: String) throws -> StripeRequest { + var body = Node([:]) + + if let owner = owner { + + if let email = owner.email { + body["owner[email]"] = Node(email) + } + + if let name = owner.name { + body["owner[name]"] = Node(name) + } + + if let phone = owner.phone { + body["owner[phone]"] = Node(phone) + } + + if let address = owner.address { + + if let line1 = address.addressLine1 { + body["owner[address][line1]"] = Node(line1) + } + + if let city = address.city { + body["owner[address][city]"] = Node(city) + } + + if let country = address.country { + body["owner[address][country]"] = Node(country) + } + + if let postalCode = address.postalCode { + body["owner[address][postal_code]"] = Node(postalCode) + } + + if let state = address.state { + body["owner[address][state]"] = Node(state) + } + + if let line2 = address.addressLine2 { + body["owner[address][line2]"] = Node(line2) + } + } + } + + if let metadata = metadata?.object { + for (key, value) in metadata { + body["metadata[\(key)]"] = value + } + } + return try StripeRequest(client: self.client, method: .post, route: .source(sourceId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil) + } +} diff --git a/Sources/API/Routes/TokenRoutes.swift b/Sources/API/Routes/TokenRoutes.swift index 7a7ad90..81ac2ac 100644 --- a/Sources/API/Routes/TokenRoutes.swift +++ b/Sources/API/Routes/TokenRoutes.swift @@ -57,8 +57,9 @@ public final class TokenRoutes { - returns: A StripeRequest<> item which you can then use to convert to the corresponding node */ - public func createCard(withCardNumber cardNumber: String, expirationMonth: Int, expirationYear: Int, cvc: Int, name: String?, customer: String? = nil, currency: StripeCurrency? = nil) throws -> StripeRequest { + public func createCardToken(withCardNumber cardNumber: String, expirationMonth: Int, expirationYear: Int, cvc: Int, name: String?, customer: String?, currency: StripeCurrency?) throws -> StripeRequest { var body = Node([:]) + var headers: [HeaderKey: String]? body["card[number]"] = Node(self.cleanNumber(cardNumber)) body["card[exp_month]"] = Node(expirationMonth) @@ -69,90 +70,56 @@ public final class TokenRoutes { body["card[name]"] = Node(name) } - if let customer = customer { - body["card[customer]"] = Node(customer) - } - if let currency = currency { body["card[currency]"] = Node(currency.rawValue) } - return try StripeRequest(client: self.client, method: .post, route: .tokens, body: Body.data(body.formURLEncoded()), headers: nil) - } - - /** - Create a payment token from a customer - - - parameter customer: The customer to generate the token with - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - public func createToken(withCustomer customer: Customer) throws -> StripeRequest { - let body = try Node(node: ["customer": customer.id]) - return try StripeRequest(client: self.client, method: .post, route: .tokens, body: Body.data(body.formURLEncoded())) - } - - /** - Create a payment token from a customer - - - parameter customerId: The customer ID to generate the token with - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - public func createToken(withCustomer customerId: String) throws -> StripeRequest { - let body = try Node(node: ["customer": customerId]) - return try StripeRequest(client: self.client, method: .post, route: .tokens, body: Body.data(body.formURLEncoded())) + // Check if we have an account to set it to + if let customer = customer { + headers = [StripeHeader.Account: customer] + } + + return try StripeRequest(client: self.client, method: .post, route: .tokens, body: Body.data(body.formURLEncoded()), headers: headers) } - /** - Retrieve a token - Retrieves the token with the given ID. - - - parameter token: The ID of the desired token. - - - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ - public func retrieve(_ token: String) throws -> StripeRequest { - return try StripeRequest(client: self.client, route: .token(token)) - } - /** Create a bank account token - Creates a single use token that wraps the details of a bank account. This token can be used in place of - a bank account dictionary with any API method. These tokens can only be used once: by attaching them to a + Creates a single use token that wraps the details of a bank account. This token can be used in place of + a bank account dictionary with any API method. These tokens can only be used once: by attaching them to a recipient or managed account. + - parameter accountNumber: The account number for the bank account in string form. Must be a checking account. + - parameter country: The country the bank account is in. - - parameter currency: The currency the bank account is in. This must be a country/currency pairing + - parameter currency: The currency the bank account is in. This must be a country/currency pairing that Stripe supports. (Re StripeCurrency enum) - - parameter accountNumber: The account number for the bank account in string form. Must be a checking account. - - - parameter routingNumber: The routing number, sort code, or other country-appropriate institution number for the + - parameter routingNumber: The routing number, sort code, or other country-appropriate institution number for the bank account. For US bank accounts, this is required and should be the ACH routing number, - not the wire routing number. If you are providing an IBAN for account_number, this field + not the wire routing number. If you are providing an IBAN for account_number, this field is not required. - - parameter accountHolderName: The name of the person or business that owns the bank account. This field is required + - parameter accountHolderName: The name of the person or business that owns the bank account. This field is required when attaching the bank account to a customer object. - - parameter accountHolderType: The type of entity that holds the account. This can be either "individual" or "company". + - parameter accountHolderType: The type of entity that holds the account. This can be either "individual" or "company". This field is required when attaching the bank account to a customer object. - - parameter customer: The customer (owned by the application's account) to create a token for. - For use with Stripe Connect only; this can only be used with an OAuth access token - or Stripe-Account header. For more details, see the shared customers documentation. + - parameter customer: The customer (owned by the application's account) to create a token for. For use with Stripe + Connect only; this can only be used with an OAuth access token or Stripe-Account header. For + more details, see the shared customers documentation. - returns: A StripeRequest<> item which you can then use to convert to the corresponding node - */ + */ - public func createBank(country: String, currency: StripeCurrency, accountNumber: String, routingNumber: String? = nil, accountHolderName: String? = nil, accountHolderType: String? = nil, customer: String? = nil) throws -> StripeRequest { + public func createBankAccountToken(withAccountNumber accountNumber: String, country: String, currency: StripeCurrency, routingNumber:String?, accountHolderName: String?, accountHolderType: String?, customer: String?) throws -> StripeRequest { var body = Node([:]) + var headers: [HeaderKey: String]? + body["bank_account[account_number]"] = Node(self.cleanNumber(accountNumber)) body["bank_account[country]"] = Node(country) body["bank_account[currency]"] = Node(currency.rawValue) - body["bank_account[account_number]"] = Node(accountNumber) if let routingNumber = routingNumber { body["bank_account[routing_number]"] = Node(routingNumber) @@ -166,10 +133,31 @@ public final class TokenRoutes { body["bank_account[account_holder_type]"] = Node(accountHolderType) } + // Check if we have an account to set it to if let customer = customer { - body["card[customer]"] = Node(customer) + headers = [StripeHeader.Account: customer] } + return try StripeRequest(client: self.client, method: .post, route: .tokens, body: Body.data(body.formURLEncoded()), headers: headers) + } + + public func createPIIToken(piiNumber: String) throws -> StripeRequest { + var body = Node([:]) + + body["pii[personal_id_number]"] = Node(piiNumber) + return try StripeRequest(client: self.client, method: .post, route: .tokens, body: Body.data(body.formURLEncoded()), headers: nil) } + + /** + Retrieve a token + Retrieves the token with the given ID. + + - parameter token: The ID of the desired token. + + - returns: A StripeRequest<> item which you can then use to convert to the corresponding node + */ + public func retrieve(_ token: String) throws -> StripeRequest { + return try StripeRequest(client: self.client, route: .token(token)) + } } diff --git a/Sources/API/StripeClient.swift b/Sources/API/StripeClient.swift index 2b51552..1296d43 100644 --- a/Sources/API/StripeClient.swift +++ b/Sources/API/StripeClient.swift @@ -18,6 +18,8 @@ public class StripeClient { public private(set) var tokens: TokenRoutes! public private(set) var refunds: RefundRoutes! public private(set) var coupons: CouponRoutes! + public private(set) var plans: PlanRoutes! + public private(set) var sources: SourceRoutes! public init(apiKey: String) throws { self.apiKey = apiKey @@ -30,6 +32,7 @@ public class StripeClient { self.tokens = TokenRoutes(client: self) self.refunds = RefundRoutes(client: self) self.coupons = CouponRoutes(client: self) + self.plans = PlanRoutes(client: self) + self.sources = SourceRoutes(client: self) } - } diff --git a/Sources/Errors/StripeError.swift b/Sources/Errors/StripeError.swift index d35d201..78f10cd 100644 --- a/Sources/Errors/StripeError.swift +++ b/Sources/Errors/StripeError.swift @@ -11,7 +11,7 @@ import Node public enum StripeError: Error { public struct ErrorInfo: NodeInitializable { - public let message: String + public private(set) var message: String? public init(node: Node) throws { self.message = try node.get("message") diff --git a/Sources/Helpers/ActionType.swift b/Sources/Helpers/ActionType.swift index 4546fcc..df2a60c 100644 --- a/Sources/Helpers/ActionType.swift +++ b/Sources/Helpers/ActionType.swift @@ -10,17 +10,6 @@ import Foundation public enum ActionType: String { case charge = "charge" - case stripeFee = "stripe_fee" - - // Payment type - case card = "card" - case bitcoin = "bitcoin" - case threeDSecure = "three_d_secure" - case giropay = "giropay" - case sepaDebit = "sepa_debit" - case ideal = "ideal" - case sofort = "sofort" - case bancontact = "bancontact" - + case stripeFee = "stripe_fee" case none = "none" } diff --git a/Sources/Helpers/RefundReason.swift b/Sources/Helpers/RefundReason.swift index ec3c1e2..c34232f 100644 --- a/Sources/Helpers/RefundReason.swift +++ b/Sources/Helpers/RefundReason.swift @@ -13,14 +13,3 @@ public enum RefundReason: String { case fraudulent = "fraudulent" case requestedByCustomer = "requested_by_customer" } - -extension RefundReason { - public init?(optionalRawValue: String?) { - if let rawValue = optionalRawValue { - if let value = RefundReason(rawValue: rawValue) { - self = value - } - } - return nil - } -} diff --git a/Sources/Helpers/SourceType.swift b/Sources/Helpers/SourceType.swift index 8b9185f..7bf1660 100644 --- a/Sources/Helpers/SourceType.swift +++ b/Sources/Helpers/SourceType.swift @@ -9,37 +9,15 @@ import Foundation import Errors -public enum SourceType { - case card(amount: Int) -} - -private enum RawSourceType: String { +public enum SourceType: String +{ case card = "card" -} - -public extension SourceType { - public init(type: String, amount: Int) throws { - guard let rawType = RawSourceType(rawValue: type.lowercased()) else { throw StripeError.invalidSourceType } - switch rawType { - case .card: self = .card(amount: amount) - } - } - - public var rawType: String { - switch self { - case .card(amount: _): return RawSourceType.card.rawValue - } - } - - public var amount: Int { - switch self { - case .card(amount: let amount): return amount - } - } - - public var dictionaryType: [String: Int] { - switch self { - case .card(amount: let amount): return [RawSourceType.card.rawValue: amount] - } - } + case bitcoin = "bitcoin" + case threeDSecure = "three_d_secure" + case giropay = "giropay" + case sepaDebit = "sepa_debit" + case ideal = "ideal" + case sofort = "sofort" + case bancontact = "bancontact" + case none = "none" } diff --git a/Sources/Helpers/StripeInterval.swift b/Sources/Helpers/StripeInterval.swift new file mode 100644 index 0000000..05f00ce --- /dev/null +++ b/Sources/Helpers/StripeInterval.swift @@ -0,0 +1,22 @@ +// +// StripeInterval.swift +// Stripe +// +// Created by Andrew Edwards on 5/29/17. +// +// + +import Foundation + +public enum StripeInterval: String { + case day = "day" + case week = "week" + case month = "month" + case year = "year" + + case never = "never" + + var description: String { + return self.rawValue.uppercased() + } +} diff --git a/Sources/Helpers/StripeStatus.swift b/Sources/Helpers/StripeStatus.swift index c443321..d394145 100644 --- a/Sources/Helpers/StripeStatus.swift +++ b/Sources/Helpers/StripeStatus.swift @@ -16,14 +16,3 @@ public enum StripeStatus: String { case cancelled = "cancelled" case chargeable = "chargeable" } - -extension StripeStatus { - public init?(optionalRawValue: String?) { - if let rawValue = optionalRawValue { - if let value = StripeStatus(rawValue: rawValue) { - self = value - } - } - return nil - } -} diff --git a/Sources/Models/Balance/Balance.swift b/Sources/Models/Balance/Balance.swift index c7c098b..873d4c3 100644 --- a/Sources/Models/Balance/Balance.swift +++ b/Sources/Models/Balance/Balance.swift @@ -10,10 +10,10 @@ import Node public final class Balance: StripeModelProtocol { - public let object: String - public let isLiveMode: Bool - public let available: [Transfer] - public let pending: [Transfer] + public private(set) var object: String? + public private(set) var isLiveMode: Bool? + public private(set) var available: [BalanceTransfer]? + public private(set) var pending: [BalanceTransfer]? public init(node: Node) throws { self.object = try node.get("object") @@ -23,12 +23,12 @@ public final class Balance: StripeModelProtocol { } public func makeNode(in context: Context?) throws -> Node { - return try Node(node: [ + let object: [String : Any?] = [ "object": self.object, "livemode": self.isLiveMode, "available": self.available, "pending": self.pending - ]) + ] + return try Node(node: object) } - } diff --git a/Sources/Models/Balance/BalanceHistoryList.swift b/Sources/Models/Balance/BalanceHistoryList.swift index d50b62c..7cdf79d 100644 --- a/Sources/Models/Balance/BalanceHistoryList.swift +++ b/Sources/Models/Balance/BalanceHistoryList.swift @@ -11,22 +11,25 @@ import Vapor public final class BalanceHistoryList: StripeModelProtocol { - public let object: String - public let hasMore: Bool - public let items: [BalanceTransactionItem] + public private(set) var object: String? + public private(set) var url: String? + public private(set) var hasMore: Bool? + public private(set) var items: [BalanceTransactionItem]? public init(node: Node) throws { self.object = try node.get("object") + self.url = try node.get("url") self.hasMore = try node.get("has_more") self.items = try node.get("data") } public func makeNode(in context: Context?) throws -> Node { - return try Node(node: [ + let object: [String : Any?] = [ "object": self.object, + "url": self.url, "has_more": self.hasMore, "data": self.items - ]) + ] + return try Node(node: object) } - } diff --git a/Sources/Models/Balance/BalanceTransactionItem.swift b/Sources/Models/Balance/BalanceTransactionItem.swift index 1eb7669..9634d04 100644 --- a/Sources/Models/Balance/BalanceTransactionItem.swift +++ b/Sources/Models/Balance/BalanceTransactionItem.swift @@ -12,17 +12,17 @@ import Helpers public final class BalanceTransactionItem: StripeModelProtocol { - public let id: String - public let object: String - public let amount: Int - public let availableOn: Date - public let created: Date - public let description: String - public let fees: [Fee] - public let net: Int - public let source: String - public let status: StripeStatus - public let type: ActionType + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amount: Int? + public private(set) var availableOn: Date? + public private(set) var created: Date? + public private(set) var description: String? + public private(set) var fees: [Fee]? + public private(set) var net: Int? + public private(set) var source: String? + public private(set) var status: StripeStatus? + public private(set) var type: ActionType? public init(node: Node) throws { self.id = try node.get("id") @@ -34,12 +34,16 @@ public final class BalanceTransactionItem: StripeModelProtocol { self.fees = try node.get("fee_details") self.net = try node.get("net") self.source = try node.get("source") - self.status = try StripeStatus(rawValue: node.get("status")) ?? StripeStatus.failed - self.type = try ActionType(rawValue: node.get("type")) ?? ActionType.none + if let status = node["status"]?.string { + self.status = StripeStatus(rawValue: status) + } + if let type = node["type"]?.string { + self.type = ActionType(rawValue: type) + } } public func makeNode(in context: Context?) throws -> Node { - return try Node(node: [ + let object: [String : Any?] = [ "id": self.id, "object": self.object, "amount": self.amount, @@ -49,10 +53,9 @@ public final class BalanceTransactionItem: StripeModelProtocol { "fee_details": self.fees, "net": self.net, "source": self.source, - "status": self.status.rawValue, - "type": self.type.rawValue - ]) + "status": self.status?.rawValue, + "type": self.type?.rawValue + ] + return try Node(node: object) } - } - diff --git a/Sources/Models/Balance/BalanceTransfer.swift b/Sources/Models/Balance/BalanceTransfer.swift new file mode 100644 index 0000000..0e5dfdd --- /dev/null +++ b/Sources/Models/Balance/BalanceTransfer.swift @@ -0,0 +1,50 @@ +// +// BalanceTransfer.swift +// Stripe +// +// Created by Anthony Castelli on 4/14/17. +// +// + +import Node +import Helpers + + +/** + Balance transfer is the body object of available array. + https://stripe.com/docs/api/curl#balance_object + */ +public class BalanceTransfer: StripeModelProtocol { + + public private(set) var currency: StripeCurrency? + public private(set) var amount: Int? + public private(set) var sourceTypes: [SourceType: Int] = [:] + + public required init(node: Node) throws { + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } + self.amount = try node.get("amount") + + let items: [String : Int] = try node.get("source_types") + + for item in items { + sourceTypes[SourceType(rawValue: item.key) ?? .none] = item.value + } + } + + public func makeNode(in context: Context?) throws -> Node { + let types = self.sourceTypes.flatMap { $0 }.reduce([String : Int]()) { dictionary, item in + var dictionary = dictionary + dictionary.updateValue(item.value, forKey: item.key.rawValue) + return dictionary + } + + let object: [String : Any?] = [ + "currency": self.currency?.rawValue, + "amount": self.amount, + "source_types": types + ] + return try Node(node: object) + } +} diff --git a/Sources/Models/Balance/Transaction.swift b/Sources/Models/Balance/Transaction.swift deleted file mode 100644 index e757f1a..0000000 --- a/Sources/Models/Balance/Transaction.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Transaction.swift -// Stripe -// -// Created by Anthony Castelli on 4/14/17. -// -// - -import Node -import Helpers - -public class Transfer: StripeModelProtocol { - - public let currency: StripeCurrency - public let amount: Int - public private(set) var sourceTypes = [SourceType]() - - public required init(node: Node) throws { - self.currency = try StripeCurrency(rawValue: node.get("currency"))! - self.amount = try node.get("amount") - let items: [String : Int] = try node.get("source_types") - self.sourceTypes = try items.map { try SourceType(type: $0.key, amount: $0.value) } - } - - public func makeNode(in context: Context?) throws -> Node { - let types = self.sourceTypes.flatMap { $0 }.reduce([String : Int]()) { dictionary, item in - var dictionary = dictionary - dictionary.updateValue(item.amount, forKey: item.rawType) - return dictionary - } - - return try Node(node: [ - "currency": self.currency.rawValue, - "amount": self.amount, - "source_types": types - ]) - } - -} diff --git a/Sources/Models/Charges/Charge.swift b/Sources/Models/Charges/Charge.swift index de1d17f..1efa321 100644 --- a/Sources/Models/Charges/Charge.swift +++ b/Sources/Models/Charges/Charge.swift @@ -16,40 +16,51 @@ import Helpers */ public final class Charge: StripeModelProtocol { - public let id: String - public let object: String - public let amount: Int - public let amountRefunded: Int - public let application: String? - public let applicationFee: Int? - public let balanceTransactionId: String? - public let isCaptured: Bool - public let created: Date - public let customerId: String? - public let description: String? - public let destination: String? - public let failureCode: Int? - public let failureMessage: String? - public let invoiceId: String? - public let isLiveMode: Bool - public let isPaid: Bool - public let isRefunded: Bool - public let review: String? - public let sourceTransfer: String? - public let statementDescriptor: String? - public let transferGroup: String? - + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amount: Int? + public private(set) var amountRefunded: Int? + public private(set) var application: String? + public private(set) var applicationFee: Int? + public private(set) var balanceTransactionId: String? + public private(set) var isCaptured: Bool? + public private(set) var created: Date? public private(set) var currency: StripeCurrency? - public private(set) var fraud: FraudDetails? + public private(set) var customerId: String? + public private(set) var destination: String? + public private(set) var dispute: String? + public private(set) var failureCode: String? + public private(set) var failureMessage: String? + public private(set) var invoiceId: String? + public private(set) var isLiveMode: Bool? + public private(set) var onBehalfOf: String? + public private(set) var order: String? public private(set) var outcome: Outcome? + public private(set) var isPaid: Bool? + public private(set) var recieptNumber: String? + public private(set) var isRefunded: Bool? public private(set) var refunds: Refund? + public private(set) var review: String? + public private(set) var source: Source? + public private(set) var card: Card? + public private(set) var sourceTransfer: String? + public private(set) var statementDescriptor: String? public private(set) var status: StripeStatus? - public private(set) var shippingLabel: ShippingLabel? - + public private(set) var transfer: String? + + /** + Only these values are mutable/updatable. + https://stripe.com/docs/api/curl#update_charge + */ + + public private(set) var description: String? + public private(set) var fraud: FraudDetails? public private(set) var metadata: Node? - - public private(set) var card: Card? - public private(set) var source: Source? + public private(set) var receiptEmail: String? + public private(set) var shippingLabel: ShippingLabel? + public private(set) var transferGroup: String? + + public init() {} public init(node: Node) throws { self.id = try node.get("id") @@ -64,23 +75,30 @@ public final class Charge: StripeModelProtocol { self.customerId = try node.get("customer") self.description = try node.get("description") self.destination = try node.get("destination") + self.dispute = try node.get("dispute") self.failureCode = try node.get("failure_code") self.failureMessage = try node.get("failure_message") self.invoiceId = try node.get("invoice") self.isLiveMode = try node.get("livemode") + self.onBehalfOf = try node.get("on_behalf_of") self.isPaid = try node.get("paid") + self.recieptNumber = try node.get("receipt_number") self.isRefunded = try node.get("refunded") self.review = try node.get("review") self.sourceTransfer = try node.get("source_transfer") self.statementDescriptor = try node.get("statement_descriptor") self.transferGroup = try node.get("transfer_group") - - self.currency = try StripeCurrency(rawValue: node.get("currency")) + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } self.fraud = try node.get("fraud_details") self.outcome = try node.get("outcome") self.refunds = try node.get("refunds") - self.status = try StripeStatus(rawValue: node.get("status")) - + if let status = node["status"]?.string { + self.status = StripeStatus(rawValue: status) + } + self.transfer = try node.get("transfer") + self.receiptEmail = try node.get("receipt_email") if let _ = node["shipping"]?.object { self.shippingLabel = try node.get("shipping") } @@ -112,14 +130,17 @@ public final class Charge: StripeModelProtocol { "customer": self.customerId, "description": self.description, "destination": self.destination, + "dispute": self.dispute, "failure_code": self.failureCode, "failure_message": self.failureMessage, "fraud_details": self.fraud, "invoice": self.invoiceId, "livemode": self.isLiveMode, + "on_behalf_of": self.onBehalfOf, "metadata": self.metadata, "outcome": self.outcome, "paid": self.isPaid, + "receipt_number": self.recieptNumber, "refunded": self.isRefunded, "refunds": self.refunds, "review": self.review, @@ -127,6 +148,8 @@ public final class Charge: StripeModelProtocol { "source_transfer": self.sourceTransfer, "statement_descriptor": self.statementDescriptor, "status": self.status?.rawValue, + "transfer": self.transfer, + "receipt_email": self.receiptEmail, "transfer_group": self.transferGroup ] @@ -137,5 +160,4 @@ public final class Charge: StripeModelProtocol { } return try Node(node: object) } - } diff --git a/Sources/Models/Charges/ChargeList.swift b/Sources/Models/Charges/ChargeList.swift index a8e9a63..7ed03c3 100644 --- a/Sources/Models/Charges/ChargeList.swift +++ b/Sources/Models/Charges/ChargeList.swift @@ -11,9 +11,9 @@ import Vapor public final class ChargeList: StripeModelProtocol { - public let object: String - public let hasMore: Bool - public let items: [Charge] + public private(set) var object: String? + public private(set) var hasMore: Bool? + public private(set) var items: [Charge]? public init(node: Node) throws { self.object = try node.get("object") @@ -22,12 +22,12 @@ public final class ChargeList: StripeModelProtocol { } public func makeNode(in context: Context?) throws -> Node { - return try Node(node: [ + let object: [String : Any?] = [ "object": self.object, "has_more": self.hasMore, "data": self.items - ]) + ] + return try Node(node: object) } - } diff --git a/Sources/Models/Charges/FruadDetails.swift b/Sources/Models/Charges/FruadDetails.swift index e0aa694..8242446 100644 --- a/Sources/Models/Charges/FruadDetails.swift +++ b/Sources/Models/Charges/FruadDetails.swift @@ -10,7 +10,7 @@ import Foundation import Vapor import Helpers -/* +/** Fraud Details https://stripe.com/docs/api/curl#charge_object-fraud_details */ @@ -21,8 +21,8 @@ public enum FraudReport: String { public final class FraudDetails: StripeModelProtocol { - public var userReport: FraudReport? - public var stripeReport: FraudReport? + public private(set) var userReport: FraudReport? + public private(set) var stripeReport: FraudReport? public init(node: Node) throws { if let value: String? = try node.get("user_report") { @@ -39,10 +39,10 @@ public final class FraudDetails: StripeModelProtocol { } public func makeNode(in context: Context?) throws -> Node { - return try Node(node: [ + let object: [String : Any?] = [ "user_report": self.userReport?.rawValue, "stripe_report": self.stripeReport?.rawValue - ]) + ] + return try Node(node: object) } - } diff --git a/Sources/Models/Charges/Outcome.swift b/Sources/Models/Charges/Outcome.swift index 38a5422..97d7e4d 100644 --- a/Sources/Models/Charges/Outcome.swift +++ b/Sources/Models/Charges/Outcome.swift @@ -10,7 +10,7 @@ import Foundation import Vapor import Helpers -/* +/** Outcome https://stripe.com/docs/api/curl#charge_object-outcome */ @@ -39,31 +39,37 @@ public enum OutcomeType: String { public final class Outcome: StripeModelProtocol { - public let networkStatus: NetworkStatus? - public let reason: String? - public let riskLevel: RiskLevel? - public let rule: String? - public let sellerMessage: String - public let type: OutcomeType? + public private(set) var networkStatus: NetworkStatus? + public private(set) var reason: String? + public private(set) var riskLevel: RiskLevel? + public private(set) var rule: String? + public private(set) var sellerMessage: String? + public private(set) var type: OutcomeType? public init(node: Node) throws { - self.networkStatus = try NetworkStatus(rawValue: node.get("network_status")) + if let networkStatus = node["network_status"]?.string { + self.networkStatus = NetworkStatus(rawValue: networkStatus) + } self.reason = try node.get("reason") - self.riskLevel = try RiskLevel(rawValue: node.get("risk_level")) + if let riskLevel = node["risk_level"]?.string { + self.riskLevel = RiskLevel(rawValue: riskLevel) + } self.rule = try node.get("rule") self.sellerMessage = try node.get("seller_message") - self.type = try OutcomeType(rawValue: node.get("type")) + if let outcome = node["type"]?.string { + self.type = OutcomeType(rawValue: outcome) + } } public func makeNode(in context: Context?) throws -> Node { - return try Node(node: [ + let object: [String : Any?] = [ "network_status": self.networkStatus?.rawValue, "reason": self.reason ?? nil, "risk_level": self.riskLevel?.rawValue, "rule": self.rule ?? nil, "seller_message": self.sellerMessage, "type": self.type?.rawValue - ]) + ] + return try Node(node: object) } - } diff --git a/Sources/Models/Charges/Refund.swift b/Sources/Models/Charges/Refund.swift index 7efde91..f9eab69 100644 --- a/Sources/Models/Charges/Refund.swift +++ b/Sources/Models/Charges/Refund.swift @@ -10,28 +10,19 @@ import Foundation import Vapor import Helpers -/* +/** Refunds https://stripe.com/docs/api/curl#charge_object-refunds */ public final class Refund: StripeModelProtocol { - public let object: String - public let hasMore: Bool - public let totalCount: Int? - public let url: String + public private(set) var object: String? + public private(set) var hasMore: Bool + public private(set) var totalCount: Int? + public private(set) var url: String? public private(set) var items: [RefundItem]? - public var id: String? { - get { - // /v1/charges/:id/refunds - let components = self.url.components(separatedBy: "/") - guard components.count > 2 else { return nil } - return components[components.count - 2] // Do a little math here - } - } - public init(node: Node) throws { self.object = try node.get("object") self.items = try node.get("data") diff --git a/Sources/Models/Charges/RefundItem.swift b/Sources/Models/Charges/RefundItem.swift index c993d87..6eeeaa4 100644 --- a/Sources/Models/Charges/RefundItem.swift +++ b/Sources/Models/Charges/RefundItem.swift @@ -12,18 +12,18 @@ import Helpers public final class RefundItem: StripeModelProtocol { - public let id: String - public let object: String - public let amount: Int - public let balanceTransactionId: String? - public let charge: String? - public let created: Date - public let currency: StripeCurrency? - public let description: String? - public let metadata: Node? - public let reason: RefundReason? - public let receiptNumber: String? - public let status: StripeStatus? + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amount: Int? + public private(set) var balanceTransactionId: String? + public private(set) var charge: String? + public private(set) var created: Date? + public private(set) var currency: StripeCurrency? + public private(set) var description: String? + public private(set) var metadata: Node? + public private(set) var reason: RefundReason? + public private(set) var receiptNumber: String? + public private(set) var status: StripeStatus? public init(node: Node) throws { self.id = try node.get("id") @@ -32,12 +32,18 @@ public final class RefundItem: StripeModelProtocol { self.balanceTransactionId = try node.get("balance_transaction") self.charge = try node.get("charge") self.created = try node.get("created") - self.currency = try StripeCurrency(rawValue: node.get("currency")) + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } self.description = try node.get("description") self.metadata = try node.get("metadata") - self.reason = try RefundReason(optionalRawValue: node.get("reason")) + if let reason = node["reason"]?.string { + self.reason = RefundReason(rawValue: reason) + } self.receiptNumber = try node.get("receipt_number") - self.status = try StripeStatus(rawValue: node.get("status")) + if let status = node["status"]?.string { + self.status = StripeStatus(rawValue: status) + } } public func makeNode(in context: Context?) throws -> Node { @@ -57,5 +63,4 @@ public final class RefundItem: StripeModelProtocol { ] return try Node(node: object) } - } diff --git a/Sources/Models/Coupons/Coupon.swift b/Sources/Models/Coupons/Coupon.swift index 4d931af..ca275d3 100644 --- a/Sources/Models/Coupons/Coupon.swift +++ b/Sources/Models/Coupons/Coupon.swift @@ -15,14 +15,14 @@ import Helpers https://stripe.com/docs/api/curl#coupon_object */ public final class Coupon: StripeModelProtocol { - public var id: String? - public var amountOff: Int? - public var currency: StripeCurrency? - public var duration: StripeDuration = .never - public var durationInMonths: Int? - public var maxRedemptions: Int? - public var percentOff: Int? - public var redeemBy: Date? + public private(set) var id: String? + public private(set) var amountOff: Int? + public private(set) var currency: StripeCurrency? + public private(set) var duration: StripeDuration? + public private(set) var durationInMonths: Int? + public private(set) var maxRedemptions: Int? + public private(set) var percentOff: Int? + public private(set) var redeemBy: Date? public private(set) var object: String? public private(set) var created: Date? public private(set) var timesRedeemed: Int? @@ -33,26 +33,21 @@ public final class Coupon: StripeModelProtocol { Only metadata is mutable/updatable. https://stripe.com/docs/api/curl#update_coupon */ - public var metadata: Node? + public private(set) var metadata: Node? - - required public init(duration: StripeDuration) { - self.duration = duration - } + public init() {} public init(node: Node) throws { self.id = try node.get("id") self.amountOff = try node.get("amount_off") self.created = try node.get("created") - if let currency = node["currency"]?.string - { + if let currency = node["currency"]?.string { self.currency = StripeCurrency(rawValue: currency) } - if let duration = node["duration"]?.string - { - self.duration = StripeDuration(rawValue: duration) ?? StripeDuration.never + if let duration = node["duration"]?.string { + self.duration = StripeDuration(rawValue: duration) } self.object = try node.get("object") self.durationInMonths = try node.get("duration_in_months") diff --git a/Sources/Models/CouponList.swift b/Sources/Models/Coupons/CouponList.swift similarity index 80% rename from Sources/Models/CouponList.swift rename to Sources/Models/Coupons/CouponList.swift index 4ac021a..8090a45 100644 --- a/Sources/Models/CouponList.swift +++ b/Sources/Models/Coupons/CouponList.swift @@ -11,10 +11,10 @@ import Vapor import Helpers public final class CouponList: StripeModelProtocol { - public let object: String - public let url: String - public let hasMore: Bool - public let items: [Coupon]? + public private(set) var object: String? + public private(set) var url: String? + public private(set) var hasMore: Bool? + public private(set) var items: [Coupon]? public init(node: Node) throws { self.object = try node.get("object") diff --git a/Sources/Models/Customer/Customer.swift b/Sources/Models/Customer/Customer.swift index bf89581..03493a9 100644 --- a/Sources/Models/Customer/Customer.swift +++ b/Sources/Models/Customer/Customer.swift @@ -16,27 +16,30 @@ import Helpers */ public final class Customer: StripeModelProtocol { - public private(set) var id: String + public private(set) var id: String? public private(set) var object: String? - public var accountBalance: Int? public private(set) var created: Date? - public var email: String? - public var bussinessVATId: String? - public var defaultSourceId: String? - public var description: String? public private(set) var delinquent: Bool? public private(set) var isLive: Bool? + public private(set) var currency: StripeCurrency? + public private(set) var sources: SourceList? + public private(set) var subscriptions: SubscriptionList? - public var metadata: Node? + /** + Only these values are mutable/updatable. + https://stripe.com/docs/api/curl#update_customer + */ - public var currency: StripeCurrency? + public private(set) var accountBalance: Int? + public private(set) var bussinessVATId: String? + public private(set) var coupon: String? + public private(set) var defaultSourceId: String? + public private(set) var description: String? + public private(set) var email: String? + public private(set) var metadata: Node? public private(set) var shipping: ShippingLabel? - public private(set) var sources: SourceList? - public private(set) var subscriptions: SubscriptionList? - public init() { - self.id = "" - } + public init() {} public init(node: Node) throws { self.id = try node.get("id") @@ -49,7 +52,7 @@ public final class Customer: StripeModelProtocol { self.description = try node.get("description") self.delinquent = try node.get("delinquent") self.isLive = try node.get("livemode") - + self.coupon = try node.get("coupon") self.metadata = try node.get("metadata") if let currency = node["currency"]?.string { @@ -75,9 +78,8 @@ public final class Customer: StripeModelProtocol { "description": self.description, "delinquent": self.delinquent, "livemode": self.isLive, - + "coupon": self.coupon, "metadata": self.metadata, - "currency": self.currency?.rawValue, "shipping": self.shipping, "sources": self.sources, @@ -85,5 +87,4 @@ public final class Customer: StripeModelProtocol { ] return try Node(node: object) } - } diff --git a/Sources/Models/Customer/CustomerList.swift b/Sources/Models/Customer/CustomerList.swift index 159b7a0..d50cd5b 100644 --- a/Sources/Models/Customer/CustomerList.swift +++ b/Sources/Models/Customer/CustomerList.swift @@ -11,10 +11,9 @@ import Vapor import Helpers public final class CustomerList: StripeModelProtocol { - - public let object: String - public let url: String - public let hasMore: Bool + public private(set) var object: String? + public private(set) var url: String? + public private(set) var hasMore: Bool? public let items: [Customer]? public init(node: Node) throws { diff --git a/Sources/Models/Fee.swift b/Sources/Models/Fee.swift index d70eb13..6a3f48f 100644 --- a/Sources/Models/Fee.swift +++ b/Sources/Models/Fee.swift @@ -12,26 +12,30 @@ import Helpers public final class Fee: StripeModelProtocol { - public let amount: Int - public let currency: StripeCurrency - public let description: String - public let type: ActionType + public private(set) var amount: Int? + public private(set) var currency: StripeCurrency? + public private(set) var description: String? + public private(set) var type: ActionType? public init(node: Node) throws { self.amount = try node.get("amount") - self.currency = try StripeCurrency(rawValue: node.get("currency"))! + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } self.description = try node.get("description") - self.type = try ActionType(rawValue: node.get("type"))! + if let type = node["type"]?.string { + self.type = ActionType(rawValue: type) + } } public func makeNode(in context: Context?) throws -> Node { - return try Node(node: [ + + let object: [String : Any?] = [ "amount": self.amount, "currency": self.currency, "description": self.description, - "type": self.type.rawValue - ]) + "type": self.type?.rawValue + ] + return try Node(node: object) } - } - diff --git a/Sources/Models/Other/DeletedObject.swift b/Sources/Models/Other/DeletedObject.swift index d207f8f..0c7c83d 100644 --- a/Sources/Models/Other/DeletedObject.swift +++ b/Sources/Models/Other/DeletedObject.swift @@ -11,7 +11,6 @@ import Vapor import Helpers public final class DeletedObject: StripeModelProtocol { - public private(set) var deleted: Bool? public private(set) var id: String? diff --git a/Sources/Models/Plans/Plans.swift b/Sources/Models/Plans/Plans.swift new file mode 100644 index 0000000..277559f --- /dev/null +++ b/Sources/Models/Plans/Plans.swift @@ -0,0 +1,79 @@ +// +// Plans.swift +// Stripe +// +// Created by Andrew Edwards on 5/29/17. +// +// + +import Foundation +import Vapor +import Helpers + +/** + Plan Model + https://stripe.com/docs/api/curl#plan_object + */ +public final class Plan: StripeModelProtocol { + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amount: Int? + public private(set) var created: Date? + public private(set) var currency: StripeCurrency? + public private(set) var interval: StripeInterval? + public private(set) var intervalCount: Int? + public private(set) var isLive: Bool? + + /** + Only these values are mutable/updatable. + https://stripe.com/docs/api/curl#update_plan + */ + + public private(set) var metadata: Node? + public private(set) var name: String? + public private(set) var statementDescriptor: String? + public private(set) var trialPeriodDays: Int? + + public init() {} + + public init(node: Node) throws { + self.id = try node.get("id") + self.object = try node.get("object") + self.amount = try node.get("amount") + self.created = try node.get("created") + + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } + + if let interval = node["interval"]?.string { + self.interval = StripeInterval(rawValue: interval) + } + + self.intervalCount = try node.get("interval_count") + self.isLive = try node.get("livemode") + self.metadata = try node.get("metadata") + self.name = try node.get("name") + self.statementDescriptor = try node.get("statement_descriptor") + self.trialPeriodDays = try node.get("trial_period_days") + } + + public func makeNode(in context: Context?) throws -> Node { + let object: [String: Any?] = [ + "id": self.id, + "object": self.object, + "amount": self.amount, + "created": self.created, + "currency": self.currency, + "interval": self.interval, + "interval_count": self.intervalCount, + "livemode": self.isLive, + "metadata": self.metadata, + "name": self.name, + "statement_descriptor": self.statementDescriptor, + "trial_period_days": self.trialPeriodDays + ] + + return try Node(node: object) + } +} diff --git a/Sources/Models/Plans/PlansList.swift b/Sources/Models/Plans/PlansList.swift new file mode 100644 index 0000000..6f26be1 --- /dev/null +++ b/Sources/Models/Plans/PlansList.swift @@ -0,0 +1,35 @@ +// +// PlansList.swift +// Stripe +// +// Created by Andrew Edwards on 5/29/17. +// +// + +import Foundation +import Vapor +import Helpers + +public final class PlansList: StripeModelProtocol { + public private(set) var object: String? + public private(set) var url: String? + public private(set) var hasMore: Bool? + public private(set) var items: [Plan]? + + public init(node: Node) throws { + self.object = try node.get("object") + self.url = try node.get("url") + 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, + "url": self.url, + "has_more": self.hasMore, + "data": self.items + ] + return try Node(node: object) + } +} diff --git a/Sources/Models/Shipping/ShippingAddress.swift b/Sources/Models/Shipping/ShippingAddress.swift index 42cd73d..59241b0 100644 --- a/Sources/Models/Shipping/ShippingAddress.swift +++ b/Sources/Models/Shipping/ShippingAddress.swift @@ -10,7 +10,7 @@ import Foundation import Vapor import Helpers -/* +/** Shipping Address https://stripe.com/docs/api/curl#charge_object-shipping-address */ @@ -46,5 +46,4 @@ public final class ShippingAddress: StripeModelProtocol { ] return try Node(node: object) } - } diff --git a/Sources/Models/Shipping/ShippingLabel.swift b/Sources/Models/Shipping/ShippingLabel.swift index 0ba7ed1..ac254e1 100644 --- a/Sources/Models/Shipping/ShippingLabel.swift +++ b/Sources/Models/Shipping/ShippingLabel.swift @@ -10,7 +10,7 @@ import Foundation import Vapor import Helpers -/* +/** Shipping https://stripe.com/docs/api/curl#charge_object-shipping */ @@ -42,5 +42,4 @@ public final class ShippingLabel: StripeModelProtocol { ] return try Node(node: object) } - } diff --git a/Sources/Models/Sources/BankAccount.swift b/Sources/Models/Sources/BankAccount.swift index f1bc187..e74b685 100644 --- a/Sources/Models/Sources/BankAccount.swift +++ b/Sources/Models/Sources/BankAccount.swift @@ -10,27 +10,38 @@ import Foundation import Vapor import Helpers -/* - https://stripe.com/docs/api/curl#create_bank_account_token +/** + Bank Account Model + https://stripe.com/docs/api/curl#customer_bank_account_object */ public final class BankAccount: StripeModelProtocol { - public let id: String - public let object: String - public let accountHolderName: String? - public let accountHolderType: String? - public let name: String? - public let country: String? - public let fingerprint: String? - public let last4: String? - public let routingNumber: String? - public let status: String? + public private(set) var id: String? + public private(set) var object: String? + public private(set) var account: String? + public private(set) var name: String? + public private(set) var country: String? + public private(set) var fingerprint: String? + public private(set) var last4: String? + public private(set) var routingNumber: String? + public private(set) var status: String? + public private(set) var customer: String? + public private(set) var defaultForCurrency: Bool? + public private(set) var currency: StripeCurrency? - public let currency: StripeCurrency? + /** + Only these values are mutable/updatable. + https://stripe.com/docs/api/curl#customer_update_bank_account + */ + + public private(set) var accountHolderName: String? + public private(set) var accountHolderType: String? + public private(set) var metadata: Node? public init(node: Node) throws { self.id = try node.get("id") self.object = try node.get("object") + self.account = try node.get("account") self.accountHolderName = try node.get("account_holder_name") self.accountHolderType = try node.get("account_holder_type") self.name = try node.get("bank_name") @@ -39,13 +50,19 @@ public final class BankAccount: StripeModelProtocol { self.last4 = try node.get("last4") self.routingNumber = try node.get("routing_number") self.status = try node.get("status") - self.currency = try StripeCurrency(rawValue: node.get("currency")) + self.customer = try node.get("customer") + self.defaultForCurrency = try node.get("default_for_currency") + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } + self.metadata = try node.get("metadata") } public func makeNode(in context: Context?) throws -> Node { let object: [String : Any?] = [ "id": self.id, "object": self.object, + "account": self.account, "account_holder_name": self.accountHolderName, "account_holder_type": self.accountHolderType, "bank_name": self.name, @@ -54,9 +71,11 @@ public final class BankAccount: StripeModelProtocol { "last4": self.last4, "routing_number": self.routingNumber, "status": self.status, - "currency": self.currency?.rawValue + "customer": self.customer, + "default_for_currency": self.defaultForCurrency, + "currency": self.currency?.rawValue, + "metadata": self.metadata ] return try Node(node: object) } - } diff --git a/Sources/Models/Sources/Card.swift b/Sources/Models/Sources/Card.swift index 4622c2d..9261dd4 100644 --- a/Sources/Models/Sources/Card.swift +++ b/Sources/Models/Sources/Card.swift @@ -10,60 +10,86 @@ import Foundation import Vapor import Helpers -/* +/** Card Model https://stripe.com/docs/api/curl#card_object */ public final class Card: StripeModelProtocol { - public let id: String - public let object: String - public let addressCity: String? - public let addressCountry: String? - public let addressLine1: String? - public let addressLine1Check: ValidationCheck? - public let addressLine2: String? - public let addressState: String? - public let addressZip: String? - public let addressZipCheck: ValidationCheck? + public private(set) var id: String? + public private(set) var object: String? + public private(set) var account: String? + public private(set) var addressLine1Check: ValidationCheck? + public private(set) var addressZipCheck: ValidationCheck? + public private(set) var country: String? + public private(set) var brand: String? + public private(set) var customerId: String? + public private(set) var cvcCheck: ValidationCheck? + public private(set) var dynamicLastFour: String? + public private(set) var fingerprint: String? + public private(set) var fundingType: FundingType? + public private(set) var lastFour: String + public private(set) var tokenizedMethod: TokenizedMethod? + public private(set) var currency: StripeCurrency? + public private(set) var defaultForCurrency: Bool? + public private(set) var recipient: String? - public let country: String - public let brand: String - public let customerId: String? - public let cvcCheck: ValidationCheck? - public let dynamicLastFour: String? - public let expirationMonth: Int - public let expirationYear: Int - public let fingerprint: String? - public let fundingType: FundingType? - public let lastFour: String - public let name: String? - public let tokenizedMethod: TokenizedMethod? - public let metadata: Node? + /** + Only these values are mutable/updatable. + https://stripe.com/docs/api/curl#update_card + */ + + public private(set) var addressCity: String? + public private(set) var addressCountry: String? + public private(set) var addressLine1: String? + public private(set) var addressLine2: String? + public private(set) var addressState: String? + public private(set) var addressZip: String? + public private(set) var expirationMonth: Int? + public private(set) var expirationYear: Int? + public private(set) var metadata: Node? + public private(set) var name: String? public init(node: Node) throws { self.id = try node.get("id") self.object = try node.get("object") + self.account = try node.get("account") self.addressCity = try node.get("address_city") self.addressCountry = try node.get("address_country") self.addressLine1 = try node.get("address_line1") - self.addressLine1Check = try ValidationCheck(optionalRawValue: node.get("address_line1_check")) + if let addressLine1Check = node["address_line1_check"]?.string { + self.addressLine1Check = ValidationCheck(optionalRawValue: addressLine1Check) + } self.addressLine2 = try node.get("address_line2") self.addressState = try node.get("address_state") self.addressZip = try node.get("address_zip") - self.addressZipCheck = try ValidationCheck(optionalRawValue: node.get("address_zip_check")) + if let addressZipCheck = node["address_zip_check"]?.string { + self.addressZipCheck = ValidationCheck(optionalRawValue: addressZipCheck) + } self.country = try node.get("country") self.brand = try node.get("brand") + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } + self.defaultForCurrency = try node.get("default_for_currency") self.customerId = try node.get("customer") - self.cvcCheck = try ValidationCheck(optionalRawValue: node.get("cvc_check")) + if let cvcCheck = node["cvc_check"]?.string { + self.cvcCheck = ValidationCheck(optionalRawValue: cvcCheck) + } self.dynamicLastFour = try node.get("dynamic_last4") self.expirationMonth = try node.get("exp_month") self.expirationYear = try node.get("exp_year") self.fingerprint = try node.get("fingerprint") - self.fundingType = try FundingType(optionalRawValue: node.get("funding")) + if let fundingType = node["funding"]?.string { + self.fundingType = FundingType(optionalRawValue: fundingType) + } self.lastFour = try node.get("last4") self.name = try node.get("name") - self.tokenizedMethod = try TokenizedMethod(optionalRawValue: node.get("tokenization_method")) + if let tokenizedMethod = node["tokenization_method"]?.string { + self.tokenizedMethod = TokenizedMethod(optionalRawValue: tokenizedMethod) + } + self.recipient = try node.get("recipient") + self.metadata = try node.get("metadata") } @@ -71,6 +97,7 @@ public final class Card: StripeModelProtocol { let object: [String : Any?] = [ "id": self.id, "object": self.object, + "account": self.account, "address_city": self.addressCity, "address_country": self.addressCountry, "address_line1": self.addressLine1, @@ -82,7 +109,9 @@ public final class Card: StripeModelProtocol { "country": self.country, "brand": self.brand, "customer": self.customerId, + "currency": self.currency?.rawValue, "cvc_check": self.cvcCheck?.rawValue, + "default_for_currency": self.defaultForCurrency, "dynamic_last4": self.dynamicLastFour, "exp_month": self.expirationMonth, "exp_year": self.expirationYear, @@ -90,10 +119,10 @@ public final class Card: StripeModelProtocol { "funding": self.fundingType?.rawValue, "last4": self.lastFour, "name": self.name, + "recipient": self.recipient, "tokenization_method": self.tokenizedMethod?.rawValue, "metadata": self.metadata ] return try Node(node: object) } - } diff --git a/Sources/Models/Sources/Owner.swift b/Sources/Models/Sources/Owner.swift index aa11276..7bafce4 100644 --- a/Sources/Models/Sources/Owner.swift +++ b/Sources/Models/Sources/Owner.swift @@ -11,17 +11,21 @@ import Vapor import Helpers public final class Owner: StripeModelProtocol { - - public let address: String? - public let email: String? - public let name: String? - public let phone: String? + public private(set) var address: ShippingAddress? + public private(set) var email: String? + public private(set) var name: String? + public private(set) var phone: String? + public private(set) var verifiedAddress: ShippingAddress? + public private(set) var verifiedEmail: String? + public private(set) var verifiedName: String? + public private(set) var verifiedPhone: String? public init(node: Node) throws { self.address = try node.get("address") self.email = try node.get("email") self.name = try node.get("name") self.phone = try node.get("phone") + self.verifiedAddress = try node.get("verified_address") } public func makeNode(in context: Context?) throws -> Node { @@ -33,5 +37,4 @@ public final class Owner: StripeModelProtocol { ] return try Node(node: object) } - } diff --git a/Sources/Models/Sources/Receiver.swift b/Sources/Models/Sources/Receiver.swift index 45c35a9..de49204 100644 --- a/Sources/Models/Sources/Receiver.swift +++ b/Sources/Models/Sources/Receiver.swift @@ -12,12 +12,12 @@ import Helpers public final class Receiver: StripeModelProtocol { - public let address: String? - public let amountCharged: Int - public let amountReceived: Int - public let amountReturned: Int - public let refundMethod: String? - public let refundStatus: String? + public private(set) var address: String? + public private(set) var amountCharged: Int? + public private(set) var amountReceived: Int? + public private(set) var amountReturned: Int? + public private(set) var refundMethod: String? + public private(set) var refundStatus: String? public init(node: Node) throws { self.address = try node.get("address") @@ -39,5 +39,4 @@ public final class Receiver: StripeModelProtocol { ] return try Node(node: object) } - } diff --git a/Sources/Models/Sources/Source.swift b/Sources/Models/Sources/Source.swift index 3898872..f202195 100644 --- a/Sources/Models/Sources/Source.swift +++ b/Sources/Models/Sources/Source.swift @@ -10,38 +10,75 @@ import Foundation import Vapor import Helpers -/* +/** Source Model https://stripe.com/docs/api/curl#source_object */ public final class Source: StripeModelProtocol { - public let id: String - public let object: String - public let amount: Int - public let clientSecret: String - public let created: Date - public let currency: StripeCurrency? - public let flow: String - public let isLive: Bool - public let status: StripeStatus? - public let type: ActionType? - public let usage: String - public let metadata: [String : Any] - public let owner: Owner? - public let reciever: Receiver? + public private(set) var id: String? + public private(set) var object: String? + public private(set) var amount: Int? + public private(set) var clientSecret: String? + public private(set) var codeVerificationStatus: String? + public private(set) var attemptsRemaining: Int? + public private(set) var created: Date? + public private(set) var currency: StripeCurrency? + public private(set) var flow: String? + public private(set) var isLive: Bool? + public private(set) var redirectReturnUrl: String? + public private(set) var redirectStatus: String? + public private(set) var redirectUrl: String? + public private(set) var status: StripeStatus? + public private(set) var type: SourceType? + public private(set) var usage: String? + public private(set) var reciever: Receiver? + public private(set) var returnedSource: [String:[String: Node]]? + + /** + Only these values are mutable/updatable. + https://stripe.com/docs/api/curl#update_source + */ + + public private(set) var metadata: Node? + public private(set) var owner: Owner? public init(node: Node) throws { self.id = try node.get("id") self.object = try node.get("object") self.amount = try node.get("amount") self.clientSecret = try node.get("client_secret") + /// We can check for code verification dictionary here + if let codeVerification = node["code_verification"]?.object { + self.attemptsRemaining = codeVerification["attempts_remaining"]?.int + self.codeVerificationStatus = codeVerification["status"]?.string + } + /// We can check for redirect dictionary here + if let redirect = node["redirect"]?.object { + self.redirectReturnUrl = redirect["return_url"]?.string + self.redirectStatus = redirect["status"]?.string + self.redirectUrl = redirect["url"]?.string + } self.created = try node.get("created") - self.currency = try StripeCurrency(rawValue: node.get("currency")) + if let currency = node["currency"]?.string { + self.currency = StripeCurrency(rawValue: currency) + } self.flow = try node.get("flow") self.isLive = try node.get("livemode") - self.status = try StripeStatus(rawValue: node.get("status")) - self.type = try ActionType(rawValue: node.get("type")) + if let status = node["status"]?.string { + self.status = StripeStatus(rawValue: status) + } + /// if we have a type we should have a body to parse + if let type = node["type"]?.string { + self.type = SourceType(rawValue: type) + if let sourceTypeBody = node["\(type)"]?.object { + var sourceBody: [String: Node] = [:] + for (key,val) in sourceTypeBody { + sourceBody["\(key)"] = val + } + self.returnedSource = ["\(type)": sourceBody] + } + } self.usage = try node.get("usage") self.metadata = try node.get("metadata") self.owner = try node.get("owner") @@ -67,5 +104,4 @@ public final class Source: StripeModelProtocol { ] return try Node(node: object) } - } diff --git a/Sources/Models/Sources/SourceList.swift b/Sources/Models/Sources/SourceList.swift index 6dc2c68..bd21a67 100644 --- a/Sources/Models/Sources/SourceList.swift +++ b/Sources/Models/Sources/SourceList.swift @@ -12,12 +12,14 @@ import Helpers public final class SourceList: StripeModelProtocol { - public let object: String - public let hasMore: Bool - public let items: [Any?]? + public private(set) var object: String? + public private(set) var url: String? + public private(set) var hasMore: Bool? + public private(set) var items: [Source]? public init(node: Node) throws { self.object = try node.get("object") + self.url = try node.get("url") self.hasMore = try node.get("has_more") self.items = try node.get("data") } @@ -25,10 +27,10 @@ public final class SourceList: StripeModelProtocol { public func makeNode(in context: Context?) throws -> Node { let object: [String : Any?] = [ "object": self.object, + "url": self.url, "has_more": self.hasMore, "data": self.items ] return try Node(node: object) } - } diff --git a/Sources/Models/Subscriptions/SubscriptionList.swift b/Sources/Models/Subscriptions/SubscriptionList.swift index b2e5fbb..0fd0db9 100644 --- a/Sources/Models/Subscriptions/SubscriptionList.swift +++ b/Sources/Models/Subscriptions/SubscriptionList.swift @@ -12,9 +12,9 @@ import Helpers public final class SubscriptionList: StripeModelProtocol { - public let object: String - public let hasMore: Bool - public let items: [Any?]? + public private(set) var object: String? + public private(set) var hasMore: Bool? + public private(set) var items: [Any?]? public init(node: Node) throws { self.object = try node.get("object") diff --git a/Sources/Models/Tokens/Token.swift b/Sources/Models/Tokens/Token.swift index e6e3fcd..de887e1 100644 --- a/Sources/Models/Tokens/Token.swift +++ b/Sources/Models/Tokens/Token.swift @@ -14,13 +14,13 @@ import Vapor */ public final class Token: StripeModelProtocol { - public let id: String - public let object: String - public let type: String - public let clientIp: String? - public let created: Date - public let isLive: Bool - public let isUsed: Bool + public private(set) var id: String? + public private(set) var object: String? + public private(set) var type: String? + public private(set) var clientIp: String? + public private(set) var created: Date? + public private(set) var isLive: Bool? + public private(set) var isUsed: Bool? public private(set) var card: Card? public private(set) var bankAccount: BankAccount? diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 0e4ec6a..334171f 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -40,12 +40,24 @@ static var allTests = [ ("testCreateCustomer", testCreateCustomer), ("testRetrieveCustomer", testRetrieveCustomer), ("testUpdateCustomer", testUpdateCustomer), + ("testAddNewSourceForCustomer", testAddNewSourceForCustomer), ("testDeleteCustomer", testDeleteCustomer), ("testRetrieveAllCustomers", testRetrieveAllCustomers), ("testFilterCustomers", testFilterCustomers), ] } +extension PlanTests { +static var allTests = [ + ("testCreatePlan", testCreatePlan), + ("testRetrievePlan", testRetrievePlan), + ("testUpdatePlan", testUpdatePlan), + ("testDeletePlan", testDeletePlan), + ("testListAllPlans", testListAllPlans), + ("testFilterPlans", testFilterPlans), +] +} + extension ProviderTests { static var allTests = [ ("testProvider", testProvider), @@ -61,11 +73,18 @@ static var allTests = [ ] } +extension SourceTests { +static var allTests = [ + ("testRetrieveSource", testRetrieveSource), + ("testUpdateSource", testUpdateSource), +] +} + extension TokenTests { static var allTests = [ - ("testTokenCreation", testTokenCreation), + ("testCardTokenCreation", testCardTokenCreation), ("testTokenRetrieval", testTokenRetrieval), - ("testBankAccountCreation", testBankAccountCreation), + ("testBankAccountTokenCreation", testBankAccountTokenCreation), ] } @@ -75,7 +94,9 @@ XCTMain([ testCase(ChargeTests.allTests), testCase(CouponTests.allTests), testCase(CustomerTests.allTests), + testCase(PlanTests.allTests), testCase(ProviderTests.allTests), testCase(RefundTests.allTests), + testCase(SourceTests.allTests), testCase(TokenTests.allTests), ]) diff --git a/Tests/StripeTests/BalanceTests.swift b/Tests/StripeTests/BalanceTests.swift index 4653ea9..a5c3bac 100644 --- a/Tests/StripeTests/BalanceTests.swift +++ b/Tests/StripeTests/BalanceTests.swift @@ -21,19 +21,32 @@ class BalanceTests: XCTestCase { do { drop = try self.makeDroplet() - let paymentToken = try drop?.stripe?.tokens.createCard(withCardNumber: "4242 4242 4242 4242", + let paymentToken = try drop?.stripe?.tokens.createCardToken(withCardNumber: "4242 4242 4242 4242", expirationMonth: 10, expirationYear: 2018, cvc: 123, - name: "Test Card") + name: "Test Card", + customer: nil, + currency: nil) .serializedResponse().id ?? "" transactionId = try drop?.stripe?.charge.create(amount: 10_00, - in: .usd, - for: .source(paymentToken), - description: "Vapor Stripe: Test Description") - .serializedResponse() - .balanceTransactionId ?? "" + in: .usd, + withFee: nil, + toAccount: nil, + capture: nil, + description: "Vapor Stripe: Test Description", + destinationAccountId: nil, + destinationAmount: nil, + transferGroup: nil, + onBehalfOf: nil, + receiptEmail: nil, + shippingLabel: nil, + customer: nil, + statementDescriptor: nil, + source: paymentToken, + metadata: nil) + .serializedResponse().balanceTransactionId ?? "" } catch { fatalError("Setup failed: \(error.localizedDescription)") } @@ -45,13 +58,13 @@ class BalanceTests: XCTestCase { } func testBalanceTransactionItem() throws { - let object = try drop?.stripe?.balance.retrieveBalance(forTransaction: transactionId).serializedResponse() + let object = try drop?.stripe?.balance.retrieveBalanceTransaction(transactionId).serializedResponse() XCTAssertNotNil(object) } func testBalanceHistory() throws { let drop = try self.makeDroplet() - let object = try drop.stripe?.balance.history().serializedResponse() + let object = try drop.stripe?.balance.history(forFilter: nil).serializedResponse() XCTAssertNotNil(object) } @@ -59,7 +72,12 @@ class BalanceTests: XCTestCase { let drop = try self.makeDroplet() let filter = StripeFilter() filter.limit = 1 - let object = try drop.stripe?.balance.history(forFilter: filter).serializedResponse() - XCTAssertEqual(object?.items.count, 1) + let balance = try drop.stripe?.balance.history(forFilter: filter).serializedResponse() + + if let balances = balance?.items { + XCTAssertEqual(balances.count, 1) + } else { + XCTFail("Balances are not present") + } } } diff --git a/Tests/StripeTests/ChargeTests.swift b/Tests/StripeTests/ChargeTests.swift index 667cdea..661043c 100644 --- a/Tests/StripeTests/ChargeTests.swift +++ b/Tests/StripeTests/ChargeTests.swift @@ -22,17 +22,31 @@ class ChargeTests: XCTestCase { do { drop = try self.makeDroplet() - let tokenId = try drop?.stripe?.tokens.createCard(withCardNumber: "4242 4242 4242 4242", + let tokenId = try drop?.stripe?.tokens.createCardToken(withCardNumber: "4242 4242 4242 4242", expirationMonth: 10, expirationYear: 2018, cvc: 123, - name: "Test Card") + name: "Test Card", + customer: nil, + currency: nil) .serializedResponse().id ?? "" chargeId = try drop?.stripe?.charge.create(amount: 10_00, in: .usd, - for: .source(tokenId), - description: "Vapor Stripe: Test Description") + withFee: nil, + toAccount: nil, + capture: true, + description: "Vapor Stripe: Test Description", + destinationAccountId: nil, + destinationAmount: nil, + transferGroup: nil, + onBehalfOf: nil, + receiptEmail: nil, + shippingLabel: nil, + customer: nil, + statementDescriptor: nil, + source: tokenId, + metadata: nil) .serializedResponse().id ?? "" } catch { fatalError("Setup failed: \(error.localizedDescription)") @@ -40,17 +54,31 @@ class ChargeTests: XCTestCase { } func testCharge() throws { - let paymentToken = try drop?.stripe?.tokens.createCard(withCardNumber: "4242 4242 4242 4242", - expirationMonth: 10, - expirationYear: 2018, - cvc: 123, - name: "Test Card") - .serializedResponse() + let paymentToken = try drop?.stripe?.tokens.createCardToken(withCardNumber: "4242 4242 4242 4242", + expirationMonth: 10, + expirationYear: 2018, + cvc: 123, + name: "Test Card", + customer: nil, + currency: nil) + .serializedResponse().id ?? "" let object = try drop?.stripe?.charge.create(amount: 10_00, in: .usd, - for: .source(paymentToken?.id ?? ""), - description: "Vapor Stripe: Test Description") + withFee: nil, + toAccount: nil, + capture: true, + description: "Vapor Stripe: Test Description", + destinationAccountId: nil, + destinationAmount: nil, + transferGroup: nil, + onBehalfOf: nil, + receiptEmail: nil, + shippingLabel: nil, + customer: nil, + statementDescriptor: nil, + source: paymentToken, + metadata: nil) .serializedResponse() XCTAssertNotNil(object) } @@ -61,7 +89,7 @@ class ChargeTests: XCTestCase { } func testListAllCharges() throws { - let object = try drop?.stripe?.charge.listAll().serializedResponse() + let object = try drop?.stripe?.charge.listAll(filter: nil).serializedResponse() XCTAssertNotNil(object) } @@ -69,10 +97,19 @@ class ChargeTests: XCTestCase { let filter = StripeFilter() filter.limit = 1 let object = try drop?.stripe?.charge.listAll(filter: filter).serializedResponse() - XCTAssertEqual(object?.items.count, 1) + XCTAssertEqual(object?.items?.count, 1) } func testChargeUpdate() throws { + + let description = "Vapor description" + + let fraudReport = try Node(node: ["user_report":FraudReport.safe.rawValue]) + let fraudDetails = try FraudDetails(node: fraudReport) + + let metadata = try Node(node:["hello": "world"]) + let receiptEmail = "vapor@stripetest.com" + let shippingAddress = ShippingAddress() shippingAddress.addressLine1 = "123 Test St" shippingAddress.addressLine2 = "456 Apt" @@ -88,31 +125,86 @@ class ChargeTests: XCTestCase { shippingLabel.trackingNumber = "1234567890" shippingLabel.address = shippingAddress - let metadata = ["test": "metadata"] - let object = try drop?.stripe?.charge.update(charge: chargeId, - metadata: metadata, - receiptEmail: "test-email@test.com", - shippingLabel: shippingLabel) - .serializedResponse() - XCTAssertNotNil(object) + let transferGroup = "Vapor group" + + let updatedCharge = try drop?.stripe?.charge.update(description: description, + fraud: fraudDetails, + receiptEmail: receiptEmail, + shippingLabel: shippingLabel, + transferGroup: transferGroup, + metadata: metadata, + charge: chargeId).serializedResponse() + + XCTAssertNotNil(updatedCharge) + + XCTAssertEqual(updatedCharge?.description, description) + + XCTAssertEqual(updatedCharge?.fraud?.userReport?.rawValue, fraudDetails.userReport?.rawValue) + + XCTAssertEqual(updatedCharge?.metadata?["hello"], metadata["hello"]) + + XCTAssertEqual(updatedCharge?.receiptEmail, receiptEmail) + + XCTAssertEqual(updatedCharge?.shippingLabel?.name, shippingLabel.name) + + XCTAssertEqual(updatedCharge?.shippingLabel?.phone, shippingLabel.phone) + + XCTAssertEqual(updatedCharge?.shippingLabel?.carrier, shippingLabel.carrier) + + XCTAssertEqual(updatedCharge?.shippingLabel?.trackingNumber, shippingLabel.trackingNumber) + + // Verify address values + + XCTAssertEqual(updatedCharge?.shippingLabel?.address?.addressLine1, shippingAddress.addressLine1) + + XCTAssertEqual(updatedCharge?.shippingLabel?.address?.addressLine2, shippingAddress.addressLine2) + + XCTAssertEqual(updatedCharge?.shippingLabel?.address?.city, shippingAddress.city) + + XCTAssertEqual(updatedCharge?.shippingLabel?.address?.state, shippingAddress.state) + + XCTAssertEqual(updatedCharge?.shippingLabel?.address?.postalCode, shippingAddress.postalCode) + + XCTAssertEqual(updatedCharge?.shippingLabel?.address?.country, shippingAddress.country) } func testChargeCapture() throws { - let paymentToken = try drop?.stripe?.tokens.createCard(withCardNumber: "4242 4242 4242 4242", - expirationMonth: 10, - expirationYear: 2018, - cvc: 123, - name: "Test Card") - .serializedResponse().id ?? "" + let paymentToken = try drop?.stripe?.tokens.createCardToken(withCardNumber: "4242 4242 4242 4242", + expirationMonth: 10, + expirationYear: 2018, + cvc: 123, + name: "Test Card", + customer: nil, + currency: nil) + .serializedResponse().id ?? "" let uncapturedCharge = try drop?.stripe?.charge.create(amount: 10_00, in: .usd, - for: .source(paymentToken), - description: "Vapor Stripe: test Description", - capture: false) + withFee: nil, + toAccount: nil, + capture: false, + description: "Vapor Stripe: Test Description", + destinationAccountId: nil, + destinationAmount: nil, + transferGroup: nil, + onBehalfOf: nil, + receiptEmail: nil, + shippingLabel: nil, + customer: nil, + statementDescriptor: nil, + source: paymentToken, + metadata: nil) .serializedResponse().id ?? "" - let object = try drop?.stripe?.charge.capture(charge: uncapturedCharge).serializedResponse() + let object = try drop?.stripe?.charge.capture(charge: uncapturedCharge, + amount: nil, + applicationFee: nil, + destinationAmount: nil, + receiptEmail: nil, + statementDescriptor: "Hello govna").serializedResponse() + XCTAssertNotNil(object) + + XCTAssert(object?.isCaptured ?? false) } } diff --git a/Tests/StripeTests/CouponTests.swift b/Tests/StripeTests/CouponTests.swift index 4e734c9..3aef0e8 100644 --- a/Tests/StripeTests/CouponTests.swift +++ b/Tests/StripeTests/CouponTests.swift @@ -12,6 +12,7 @@ import XCTest @testable import Vapor @testable import Helpers @testable import API +@testable import Models class CouponTests: XCTestCase { var drop: Droplet? @@ -55,12 +56,13 @@ class CouponTests: XCTestCase { } func testUpdateCoupon() throws { - let metadata = ["hello":"world"] + + let metadata = try Node(node:["hello":"world"]) let updatedCoupon = try drop?.stripe?.coupons.update(metadata: metadata, forCouponId: couponId).serializedResponse() XCTAssertNotNil(updatedCoupon) - XCTAssert(updatedCoupon?.metadata?["hello"] == "world") + XCTAssertEqual(updatedCoupon?.metadata?["hello"], metadata["hello"]) } func testDeleteCoupon() throws { @@ -72,7 +74,7 @@ class CouponTests: XCTestCase { } func testListAllCoupons() throws { - let coupons = try drop?.stripe?.coupons.listAll().serializedResponse() + let coupons = try drop?.stripe?.coupons.listAll(filter: nil).serializedResponse() XCTAssertNotNil(coupons) diff --git a/Tests/StripeTests/CustomerTests.swift b/Tests/StripeTests/CustomerTests.swift index bed487e..f1ce43d 100644 --- a/Tests/StripeTests/CustomerTests.swift +++ b/Tests/StripeTests/CustomerTests.swift @@ -22,23 +22,33 @@ class CustomerTests: XCTestCase { do { drop = try self.makeDroplet() - let customer = Customer() - customer.email = "test@stripetest.com" - customer.description = "This is a test customer" - - customerId = try drop?.stripe?.customer.create(customer: customer).serializedResponse().id ?? "" + customerId = try drop?.stripe?.customer.create(accountBalance: nil, + businessVATId: nil, + coupon: nil, + defaultSource: nil, + description: "Vapor test Account", + email: "vapor@stripetest.com", + shipping: nil, + source: nil, + metadata: nil).serializedResponse().id ?? "" } catch { fatalError("Setup failed: \(error.localizedDescription)") } } func testCreateCustomer() throws { - let customer = Customer() - customer.email = "test@stripetest.com" - customer.description = "This is a test customer" - let object = try drop?.stripe?.customer.create(customer: customer).serializedResponse() + + let object = try drop?.stripe?.customer.create(accountBalance: nil, + businessVATId: nil, + coupon: nil, + defaultSource: nil, + description: "Vapor test Account", + email: "vapor@stripetest.com", + shipping: nil, + source: nil, + metadata: nil).serializedResponse() XCTAssertNotNil(object) } @@ -48,20 +58,70 @@ class CustomerTests: XCTestCase { } func testUpdateCustomer() throws { - let customer = Customer() - customer.email = "tester@stripetest.com" - customer.description = "This is a test customer updated" - let object = try drop?.stripe?.customer.update(customer: customer, forCustomerId: customerId).serializedResponse() - XCTAssertNotNil(object) + + let updatedDescription = "Updated Vapor test Account" + + let updatedAccountBalance = 20_00 + + let metadata = try Node(node:["hello":"world"]) + + let updatedCustomer = try drop?.stripe?.customer.update(accountBalance: updatedAccountBalance, + businessVATId: nil, + coupon: nil, + defaultSourceId: nil, + description: updatedDescription, + email: nil, + shipping: nil, + newSource: nil, + metadata: metadata, + forCustomerId: customerId).serializedResponse() + XCTAssertNotNil(updatedCustomer) + + XCTAssertEqual(updatedCustomer?.description, updatedDescription) + + XCTAssertEqual(updatedCustomer?.accountBalance, updatedAccountBalance) + + XCTAssertEqual(updatedCustomer?.metadata?["hello"], metadata["hello"]) + } + + func testAddNewSourceForCustomer() throws { + + let paymentTokenSource = try drop?.stripe?.tokens.createCardToken(withCardNumber: "4242 4242 4242 4242", + expirationMonth: 10, + expirationYear: 2018, + cvc: 123, + name: "Test Card", + customer: nil, + currency: nil) + .serializedResponse().id ?? "" + + let newCardToken = try drop?.stripe?.customer.addNewSource(for: customerId, + inConnectAccount: nil, + source: paymentTokenSource) + .serializedResponse() + + XCTAssertNotNil(newCardToken) + + let updatedCustomer = try drop?.stripe?.customer.retrieve(customer: customerId).serializedResponse() + + XCTAssertNotNil(updatedCustomer) + + let customerCardSource = updatedCustomer?.sources?.items?.filter { $0.id == newCardToken?.id}.first + + XCTAssertNotNil(customerCardSource) + + XCTAssertEqual(newCardToken?.id, customerCardSource?.id) } + + func testDeleteCustomer() throws { let object = try drop?.stripe?.customer.delete(customer: customerId).serializedResponse() XCTAssertEqual(object?.deleted, true) } func testRetrieveAllCustomers() throws { - let object = try drop?.stripe?.customer.listAll().serializedResponse() + let object = try drop?.stripe?.customer.listAll(filter: nil).serializedResponse() XCTAssertGreaterThanOrEqual(object!.items!.count, 1) } diff --git a/Tests/StripeTests/PlanTests.swift b/Tests/StripeTests/PlanTests.swift new file mode 100644 index 0000000..23f9d3d --- /dev/null +++ b/Tests/StripeTests/PlanTests.swift @@ -0,0 +1,120 @@ +// +// PlanTests.swift +// Stripe +// +// Created by Andrew Edwards on 5/29/17. +// +// + +import XCTest + +@testable import Stripe +@testable import Vapor +@testable import Helpers +@testable import API +@testable import Models + +class PlanTests: XCTestCase { + var drop: Droplet? + var planId: String = "" + + override func setUp() { + do { + drop = try self.makeDroplet() + + planId = try drop?.stripe?.plans.create(id: TestUtil.randomString(8), + amount: 10_00, + currency: .usd, + interval: .week, + name: "Test Plan", + intervalCount: 5, + statementDescriptor: "Test Plan", + trialPeriodDays: nil, + metadata: nil) + .serializedResponse().id ?? "" + } catch { + fatalError("Setup failed: \(error.localizedDescription)") + } + } + + func testCreatePlan() throws { + let plan = try drop?.stripe?.plans.create(id: TestUtil.randomString(8), + amount: 10_00, + currency: .usd, + interval: .week, + name: "Test Plan", + intervalCount: 5, + statementDescriptor: "Test Plan", + trialPeriodDays: nil, + metadata: nil) + .serializedResponse().id ?? "" + XCTAssertNotNil(plan) + } + + func testRetrievePlan() throws { + let plan = try drop?.stripe?.plans.retrieve(plan: planId) + + XCTAssertNotNil(plan) + } + + func testUpdatePlan() throws { + + let metadata = try Node(node:["hello":"world"]) + let newName = "Super plan" + let newStatementDescriptor = "Super descriptor" + let trialDays = 30 + + let updatedPlan = try drop?.stripe?.plans.update(name: newName, + statementDescriptor: newStatementDescriptor, + trialPeriodDays: trialDays, + metadata: metadata, + forPlanId: planId) + .serializedResponse() + + XCTAssertNotNil(updatedPlan) + + XCTAssertEqual(updatedPlan?.metadata?["hello"], metadata["hello"]) + + XCTAssertEqual(updatedPlan?.name, newName) + + XCTAssertEqual(updatedPlan?.statementDescriptor, newStatementDescriptor) + + XCTAssertEqual(updatedPlan?.trialPeriodDays, trialDays) + } + + func testDeletePlan() throws { + let deletedPlan = try drop?.stripe?.plans.delete(plan: planId).serializedResponse() + + XCTAssertNotNil(deletedPlan) + + XCTAssertTrue(deletedPlan?.deleted ?? false) + } + + func testListAllPlans() throws { + let plans = try drop?.stripe?.plans.listAll(filter: nil).serializedResponse() + + XCTAssertNotNil(plans) + + if let planItems = plans?.items { + XCTAssertGreaterThanOrEqual(planItems.count, 1) + } else { + XCTFail("Plans are not present") + } + } + + func testFilterPlans() throws { + let filter = StripeFilter() + + filter.limit = 1 + + let plans = try drop?.stripe?.plans.listAll(filter: filter).serializedResponse() + + XCTAssertNotNil(plans) + + if let planItems = plans?.items { + XCTAssertEqual(planItems.count, 1) + } else { + XCTFail("Plans are not present") + } + } +} diff --git a/Tests/StripeTests/RefundTests.swift b/Tests/StripeTests/RefundTests.swift index 73f67f8..a2b0ce8 100644 --- a/Tests/StripeTests/RefundTests.swift +++ b/Tests/StripeTests/RefundTests.swift @@ -11,6 +11,7 @@ import XCTest @testable import Vapor @testable import Models @testable import API +@testable import Helpers class RefundTests: XCTestCase { @@ -21,18 +22,40 @@ class RefundTests: XCTestCase { do { drop = try self.makeDroplet() - let tokenId = try drop?.stripe?.tokens.createCard(withCardNumber: "4242 4242 4242 4242", - expirationMonth: 10, - expirationYear: 2018, - cvc: 123, - name: "Test Card").serializedResponse().id ?? "" + let cardToken = try drop?.stripe?.tokens.createCardToken(withCardNumber: "4242 4242 4242 4242", + expirationMonth: 10, + expirationYear: 2018, + cvc: 123, + name: "Test Card", + customer: nil, + currency: nil) + .serializedResponse().id ?? "" let chargeId = try drop?.stripe?.charge.create(amount: 10_00, - in: .usd, - for: .source(tokenId), - description: "Vapor Stripe: Test Description").serializedResponse().id ?? "" + in: .usd, + withFee: nil, + toAccount: nil, + capture: true, + description: "Vapor Stripe: Test Description", + destinationAccountId: nil, + destinationAmount: nil, + transferGroup: nil, + onBehalfOf: nil, + receiptEmail: nil, + shippingLabel: nil, + customer: nil, + statementDescriptor: nil, + source: cardToken, + metadata: nil) + .serializedResponse().id ?? "" - refundId = try drop?.stripe?.refunds.refund(charge: chargeId).serializedResponse().id ?? "" + refundId = try drop?.stripe?.refunds.createRefund(charge: chargeId, + amount: 5_00, + reason: .requestedByCustomer, + refundApplicationFee: nil, + reverseTransfer: nil, + metadata: nil) + .serializedResponse().id ?? "" } catch { fatalError("Setup failed: \(error.localizedDescription)") } @@ -40,33 +63,77 @@ class RefundTests: XCTestCase { func testRefunding() throws { - let paymentToken = try drop?.stripe?.tokens.createCard(withCardNumber: "4242 4242 4242 4242", - expirationMonth: 10, - expirationYear: 2018, - cvc: 123, - name: "Test Card").serializedResponse().id ?? "" + let cardToken = try drop?.stripe?.tokens.createCardToken(withCardNumber: "4242 4242 4242 4242", + expirationMonth: 10, + expirationYear: 2018, + cvc: 123, + name: "Test Card", + customer: nil, + currency: nil) + .serializedResponse().id ?? "" let charge = try drop?.stripe?.charge.create(amount: 10_00, in: .usd, - for: .source(paymentToken), - description: "Vapor Stripe: Test Description").serializedResponse().id ?? "" + withFee: nil, + toAccount: nil, + capture: true, + description: "Vapor Stripe: Test Description", + destinationAccountId: nil, + destinationAmount: nil, + transferGroup: nil, + onBehalfOf: nil, + receiptEmail: nil, + shippingLabel: nil, + customer: nil, + statementDescriptor: nil, + source: cardToken, + metadata: nil) + .serializedResponse().id ?? "" - let object = try drop?.stripe?.refunds.refund(charge: charge).serializedResponse() - XCTAssertNotNil(object) + let metadata = Node(["hello":"world"]) + + let refund = try drop?.stripe?.refunds.createRefund(charge: charge, + amount: 5_00, + reason: .requestedByCustomer, + refundApplicationFee: false, + reverseTransfer: false, + metadata: metadata) + .serializedResponse() + XCTAssertNotNil(refund) + + XCTAssertEqual(refund?.metadata?.object?["hello"], metadata["hello"]) + + XCTAssertEqual(refund?.amount, 5_00) + + XCTAssertEqual(refund?.reason, .requestedByCustomer) } func testUpdatingRefund() throws { - let object = try drop?.stripe?.refunds.update(refund: refundId, metadata: ["test": "Test Updating a charge"]).serializedResponse() - XCTAssertNotNil(object) + + let metadata = Node(["hello":"world"]) + + let updatedRefund = try drop?.stripe?.refunds.update(metadata: metadata, + refund: refundId) + .serializedResponse() + XCTAssertNotNil(updatedRefund) + + XCTAssertEqual(updatedRefund?.metadata?.object?["hello"], metadata["hello"]) + + XCTAssertEqual(updatedRefund?.amount, 5_00) } func testRetrievingRefund() throws { - let object = try drop?.stripe?.refunds.retrieve(refund: refundId).serializedResponse() - XCTAssertNotNil(object) + let refund = try drop?.stripe?.refunds.retrieve(refund: refundId).serializedResponse() + XCTAssertNotNil(refund) } func testListingAllRefunds() throws { - let object = try drop?.stripe?.refunds.listAll().serializedResponse() - XCTAssertGreaterThanOrEqual(object!.items!.count, 1) + let refunds = try drop?.stripe?.refunds.listAll(byChargeId: nil, filter: nil).serializedResponse() + + if let refundItems = refunds?.items { + XCTAssertGreaterThanOrEqual(refundItems.count, 1) + } else { + XCTFail("Refunds are not present") + } } } diff --git a/Tests/StripeTests/SourceTests.swift b/Tests/StripeTests/SourceTests.swift new file mode 100644 index 0000000..ed8a6a3 --- /dev/null +++ b/Tests/StripeTests/SourceTests.swift @@ -0,0 +1,67 @@ +// +// SourceTests.swift +// Stripe +// +// Created by Andrew Edwards on 6/2/17. +// +// + +import XCTest + +@testable import Stripe +@testable import Vapor +@testable import Models +@testable import API +@testable import Helpers + +class SourceTests: XCTestCase { + + var drop: Droplet? + var sourceId: String = "" + override func setUp() { + do { + drop = try self.makeDroplet() + + let card: [String: Node] = ["number":"4242 4242 4242 4242", "cvc":123,"exp_month":10, "exp_year":2018] + + sourceId = try drop?.stripe?.sources.createNewSource(sourceType: .card, + source: card, + amount: nil, + currency: nil, + flow: nil, + owner: nil, + redirectReturnUrl: nil, + token: nil, + usage: nil, + metadata: nil).serializedResponse().id ?? "" + + } catch { + fatalError("Setup failed: \(error.localizedDescription)") + } + } + + func testRetrieveSource() throws { + let retrievedSource = try drop?.stripe?.sources.retrieveSource(withId: sourceId).serializedResponse() + + XCTAssertNotNil(retrievedSource) + + XCTAssertEqual(retrievedSource?.type, SourceType.card) + + XCTAssertNotNil(retrievedSource?.returnedSource) + } + + func testUpdateSource() throws { + + let metadata = try Node(node:["hello": "world"]) + + let updatedSource = try drop?.stripe?.sources.update(owner: nil, + metadata: metadata, + forSourceId: sourceId).serializedResponse() + + XCTAssertNotNil(updatedSource) + + XCTAssertEqual(updatedSource?.metadata?["hello"], metadata["hello"]) + + XCTAssertEqual(updatedSource?.type, SourceType.card) + } +} diff --git a/Tests/StripeTests/TestUtil.swift b/Tests/StripeTests/TestUtil.swift new file mode 100644 index 0000000..0504dfc --- /dev/null +++ b/Tests/StripeTests/TestUtil.swift @@ -0,0 +1,41 @@ +// +// TestUtil.swift +// Stripe +// +// Created by Andrew Edwards on 5/29/17. +// +// + +import Foundation + +public final class TestUtil { + + public static func randomString(_ length: Int) -> String { + + let letters : String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + let len = letters.characters.count + + var randomString = "" + + #if os(Linux) + + srand(UInt32(time(nil))) + + for _ in 0..