Skip to content

Commit

Permalink
customers: Implement customers
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonycastelli committed Apr 20, 2017
1 parent 2dc8d7e commit 99193f7
Show file tree
Hide file tree
Showing 17 changed files with 535 additions and 14 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Vapor Strip Provider
# Vapor Stripe Provider

![Stripe API Coverage](https://img.shields.io/badge/stripe%20api%20coverage-15%25-red.svg)
![Swift](http://img.shields.io/badge/swift-3.1-brightgreen.svg)
![Vapor](http://img.shields.io/badge/vapor-2.0-brightgreen.svg)
![Build](https://img.shields.io/badge/build-passing-brightgreen.svg)
Expand Down Expand Up @@ -47,7 +46,12 @@ The object is returned response model, or model array.
* [x] Listing all charges
* [x] Updating a charge
* [x] Capturing a charge
* [ ] Customers
* [x] Customers
* [x] Creating
* [x] Updating
* [x] Deleting
* [x] Fetching by Customer ID
* [x] Listing All Customers (With filters)
* [ ] Disputes
* [ ] Events
* [ ] Refunds
Expand Down
15 changes: 13 additions & 2 deletions Sources/API/Helpers/Endpoints.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,23 @@ internal enum API {
case balanceHistoryTransaction(String)

/**
Charges

CHARGES
To charge a credit or a debit card, you create a charge object. You can retrieve and refund
individual charges as well as list all charges. Charges are identified by a unique random ID.
*/
case charges
case charge(String)
case captureCharge(String)

/**
CUSTOMERS
Customers allow you to perform recurring charges and track multiple charges that are
associated with the same customer. The API allows you to create, delete, and update your customers.
You can retrieve individual customers as well as a list of all your customers.
*/
case customers
case customer(String)

var endpoint: String {
switch self {
case .balance: return APIBase + APIVersion + "balance"
Expand All @@ -56,6 +64,9 @@ internal enum API {
case .charges: return APIBase + APIVersion + "charges"
case .charge(let id): return APIBase + APIVersion + "charges/\(id)"
case .captureCharge(let id): return APIBase + APIVersion + "charges/\(id)/capture"

case .customers: return APIBase + APIVersion + "customers"
case .customer(let id): return APIBase + APIVersion + "customers/\(id)"
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/API/Routes/BalanceRoutes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Models

public final class BalanceRoutes {

var client: StripeClient!
let client: StripeClient

init(client: StripeClient) {
self.client = client
Expand Down
2 changes: 1 addition & 1 deletion Sources/API/Routes/ChargeRoutes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public enum ChargeType {

public final class ChargeRoutes {

var client: StripeClient!
let client: StripeClient

init(client: StripeClient) {
self.client = client
Expand Down
193 changes: 193 additions & 0 deletions Sources/API/Routes/CustomerRoutes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
//
// CustomerRoutes.swift
// Stripe
//
// Created by Anthony Castelli on 4/20/17.
//
//

import Foundation
import Node
import HTTP
import Models
import Helpers
import Errors

public final class CustomerRoutes {

let client: StripeClient

init(client: StripeClient) {
self.client = client
}

/**
Create a customer
Creates a new customer object.

- 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 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<Customer> {
var body = Node([:])

body["email"] = Node(email)

if let currency = currency {
body["curency"] = Node(currency.rawValue)
}

if let description = description {
body["description"] = Node(description)
}

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: .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<Customer> {
var body = Node([:])

if let email = customer.email {
body["email"] = Node(email)
}

if let description = customer.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 metadata = customer.metadata {
for (key, value) in metadata {
body["metadata[\(key)]"] = try Node(node: "\(value)")
}
}

return try StripeRequest(client: self.client, method: .post, route: .customers, 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
that was returned upon customer creation.

- parameter customerId: The Customer's ID

- returns: A StripeRequest<> item which you can then use to convert to the corresponding node
*/
public func retrieve(customer customerId: String) throws -> StripeRequest<Customer> {
return try StripeRequest(client: self.client, method: .get, route: .customer(customerId), query: [:], body: nil, headers: nil)
}

/**
Update a customer
Updates the specified customer by setting the values of the parameters passed. Any parameters not
provided will be left unchanged. For example, if you pass the source parameter, that becomes the
customer’s active source (e.g., a card) to be used for all charges in the future. When you update a
customer to a new valid source: for each of the customer’s current subscriptions, if the subscription
is in the past_due state, then the latest unpaid, unclosed invoice for the subscription will be retried
(note that this retry will not count as an automatic retry, and will not affect the next regularly
scheduled payment for the invoice). (Note also that no invoices pertaining to subscriptions in the
unpaid state, or invoices pertaining to canceled subscriptions, will be retried as a result of updating
the customer’s source.)

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

- 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<Customer> {
var body = Node([:])

if let email = customer.email {
body["email"] = Node(email)
}

if let description = customer.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 metadata = customer.metadata {
for (key, value) in metadata {
body["metadata[\(key)]"] = try Node(node: "\(String(describing: value))")
}
}

return try StripeRequest(client: self.client, method: .post, route: .customer(customerId), query: [:], body: Body.data(body.formURLEncoded()), headers: nil)
}

/**
Delete a customer
Permanently deletes a customer. It cannot be undone. Also immediately cancels any active subscriptions on the customer.

- parameter customerId: The Customer's ID

- returns: A StripeRequest<> item which you can then use to convert to the corresponding node
*/
public func delete(customer customerId: String) throws -> StripeRequest<DeletedObject> {
return try StripeRequest(client: self.client, method: .delete, route: .customer(customerId), query: [:], body: nil, headers: nil)
}

/**
List all customers
Returns a list of your customers. The customers are returned sorted by creation date, with the
most recent customers appearing first.

- parameter filter: A Filter item to ass query parameters when fetching results

- returns: A StripeRequest<> item which you can then use to convert to the corresponding node
*/
public func listAll(filter: Filter?=nil) throws -> StripeRequest<CustomerList> {
var query = [String : NodeRepresentable]()
if let data = try filter?.createQuery() {
query = data
}
return try StripeRequest(client: self.client, method: .get, route: .customers, query: query, body: nil, headers: nil)
}
}
2 changes: 2 additions & 0 deletions Sources/API/StripeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class StripeClient {

public private(set) var balance: BalanceRoutes!
public private(set) var charge: ChargeRoutes!
public private(set) var customer: CustomerRoutes!

public init(apiKey: String) throws {
self.apiKey = apiKey
Expand All @@ -22,6 +23,7 @@ public class StripeClient {
public func initializeRoutes() {
self.balance = BalanceRoutes(client: self)
self.charge = ChargeRoutes(client: self)
self.customer = CustomerRoutes(client: self)
}

}
1 change: 1 addition & 0 deletions Sources/Errors/StripeError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ public enum StripeError: Error {

// Other
case invalidSourceType
case missingParamater(String)
}
87 changes: 87 additions & 0 deletions Sources/Models/Customer/Customer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// Customer.swift
// Stripe
//
// Created by Anthony Castelli on 4/19/17.
//
//

import Foundation
import Vapor
import Helpers

/**
Customer Model
https://stripe.com/docs/api/curl#customer_object
*/
public final class Customer: StripeModelProtocol {

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 var metadata: [String : Any?]?

public var currency: StripeCurrency?
public private(set) var shipping: ShippingLabel?
public private(set) var sources: SourceList?
public private(set) var subscriptions: SubscriptionList?

public init() { }

public init(node: Node) throws {
self.id = try node.get("id")
self.object = try node.get("object")
self.accountBalance = try node.get("account_balance")
self.created = try node.get("created")
self.email = try node.get("email")
self.bussinessVATId = try node.get("business_vat_id")
self.defaultSourceId = try node.get("default_source")
self.description = try node.get("description")
self.delinquent = try node.get("delinquent")
self.isLive = try node.get("livemode")

self.metadata = try node.get("metadata")

if let currency = node["currency"]?.string {
self.currency = StripeCurrency(rawValue: currency)
}

if let _ = node["shipping"]?.object {
self.shipping = try node.get("shipping")
}

self.sources = try node.get("sources")
self.subscriptions = try node.get("subscriptions")
}

public func makeNode(in context: Context?) throws -> Node {
let object: [String : Any?] = [
"id": self.id,
"object": self.object,
"account_balance": self.accountBalance,
"created": self.created,
"email": self.email,
"business_vat_id": self.bussinessVATId,
"description": self.description,
"delinquent": self.delinquent,
"livemode": self.isLive,

"metadata": self.metadata,

"currency": self.currency?.rawValue,
"shipping": self.shipping,
"sources": self.sources,
"subscriptions": self.subscriptions
]
return try Node(node: object)
}

}
Loading

0 comments on commit 99193f7

Please sign in to comment.