Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
prachetasp committed Nov 19, 2014
0 parents commit dca3ce1
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
@@ -0,0 +1,10 @@
pom.xml
*jar
/lib/
/classes/
.lein-deps-sum
*~
/src/clj-stripe/stripe.cjl*
.nrepl-port
.lein-failures
core_config.clj
71 changes: 71 additions & 0 deletions README.md
@@ -0,0 +1,71 @@
stripe-clojure
================================

stripe-clojure is intended as a lightweight wrapper around the [Stripe API](https://stripe.com/docs/api "Stripe API Documentation")

Usage
================================

stripe-clojure attempts to leverage functional style by allowing the user to call actions on map's. The action's names are those found in the Stripe API docs e.g. (retrieve, create, delete, update etc.) The data contained in each map also uses the same naming scheme found in the Stripe docs.

The first step is to add the library to the project.clj file:

```
[prachetasp/stripe-clojure "1.0.0"]
```

Everything is located in the `stripe-clojure.core` namespace. Add to your namespace declaration:

```
(:require [stripe-clojure.core :as s])
```

The authentication token must be set so it can be used in calls to the API. stripe-clojure currently only supports using the private token. To set the token:

```
(set-tokens! {:private "sk_test_fkjdskfjdskfjdslkf"})
```

Once the token has been set you can execute operations against the API.

Currently supported operations are: `cancel`, `create`, `delete`, `retrieve`, `list`, and `update`

These operations accept a map of a map of data keyed by the resource. For example:

```
{:customers {:customer_id "cus_5An5UPQPrSaS9e", :email "mrclojure@stripetest.com", :description "customer test"}}
```

Currently supported resources are: `cards`, `customers`, `events`, `invoices`, `plans`, `subscriptions`, and `tokens`

Putting it all together to update an existing user:

```
(s/update {:customers {:customer_id "cus_5An5UPQPrSaS9e", :email "mrclojure@stripetest.com", :description "customer test"}})
```

The details of the data vary for different resources and operations but the names (keys in the map) are always the same as in the stripe documentation.

A successful request will return a map of the json data returned from Stripe. A failure will return a map containing the type of error and message. The general structure of an error map is:

```
{:error {:type "invalid_request_error", :message "Received unknown parameter: fail", :param "fail"}}
```

Therefore, testing for :error in the response allows you to identify and handle errors.

Contributing
================================

Due to the lightweight nature of the library contributing is easy. Simply make your changes, write a test (if applicable) and submit a PR. The goals of the library are to remain small and fast. Changes that increase performance are encouraged!

To run the tests copy `/test/stripe_clojure/test/core_config.clj.example` to `/test/stripe_clojure/test/core_config.clj` and add the private authentication token to the map `secret-tokens`. core_config.clj is included in the .gitignore file to prevent accidentally leaking tokens.

Tests can then be run with `lein test`.

License
================================

Copyright (C) 2014 Prachetas Prabhu

Distributed under the Eclipse Public License.
6 changes: 6 additions & 0 deletions project.clj
@@ -0,0 +1,6 @@
(defproject prachetasp/stripe-clojure "1.0.0-SNAPSHOT"
:description "Clojure Stripe Library"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
[clj-http "0.9.1"]])
76 changes: 76 additions & 0 deletions src/stripe_clojure/core.clj
@@ -0,0 +1,76 @@
(ns stripe-clojure.core
"Functions for Stripe Customers API"
(:require [clj-http.client :as client])
(:refer-clojure :exclude (list)))

;; TODO: add support for public token selection?
(def stripe-tokens (atom {:public "" :private ""}))
(defn set-tokens! [m] (swap! stripe-tokens (fn [a] (merge a m))))

(def stripe-api-url "https://api.stripe.com/v1/")
(defn build-url [[url-ks url-vs] params]
(str stripe-api-url
(apply str
(interpose "/"
(filter (comp not nil?)
(interleave url-ks (map params url-vs)))))))

(defn make-request
[params method resource]
(:body (method (build-url resource params)
{:basic-auth [(:private @stripe-tokens)]
:query-params (apply dissoc params (second resource))
:throw-exceptions false
:as :json
:coerce :always})))

(def url-vals {"cards" :card_id
;;"charges" :charge_id
;;"coupons" :coupon_id
"customers" :customer_id
"events" :event_id
;;"invoiceitems" :invoiceitem_id
"invoices" :invoice_id
"plans" :plan_id
"subscriptions" :subscription_id
"tokens" :token_id})

;; resources lacking test coverage are commented out
;; map like {:cards [["customers" "cards"] [:customer_id :card_id]]...}
;; second vector values can also be nil
(def url-mapping (into {}
(map (fn [[k v]] [k [v (map url-vals v)]])
{:cards ["customers" "cards"]
;;:charges ["charges"]
;;:capture ["charges" "capture"] ; no-id endpt
;;:refund ["charges" "refund"] ; no-id endpt
;;:coupons ["coupons"]
:customers ["customers"]
;;:discounts ["customers" "discount"] ; no-id endpt
:events ["events"]
;;:invoiceitems ["invoiceitems"]
:invoices ["invoices"]
;;:lines ["invoices" "lines"] ; no-id endpt
;;:pay ["invoices" "pay"] ; no id endpt
;;:upcoming ["invoices" "upcoming"] ; no-id endpt
:plans ["plans"]
:subscriptions ["customers" "subscriptions"]
:tokens ["tokens"]})))

(defmacro defop
"Creates function that accepts a map of resource keyword to map of params"
[op-name http-action]
`(defn ~op-name [resource#]
(let [[kw# params#] (first resource#)]
(make-request params# ~http-action (url-mapping kw#)))))

;; operations lacking test coverage are commented out
(defop cancel client/delete)
#_(defop capture client/post)
(defop create client/post)
(defop delete client/delete)
(defop retrieve client/get)
(defop list client/get)
#_(defop pay client/post)
#_(defop refund client/post)
(defop update client/post)
49 changes: 49 additions & 0 deletions test/stripe_clojure/test/core.clj
@@ -0,0 +1,49 @@
(ns stripe-clojure.test.core
(:require [stripe-clojure.core :as s])
(:require [stripe-clojure.test.core-config :refer [secret-tokens]])
(:require [clojure.test :refer [deftest is testing]]))

(deftest test-set-tokens!
(testing "set-tokens!"
(testing "private and public" (is (= (s/set-tokens! {:private "private" :public "public"}) {:private "private" :public "public"})))
(testing "private only" (is (= (s/set-tokens! {:private "privater"}) {:private "privater" :public "public"})))
(testing "public only" (is (= (s/set-tokens! {:public "publicer"}) {:private "privater" :public "publicer"})))
(testing "empty" (is (= (s/set-tokens! {}) {:private "privater" :public "publicer"})))))

(deftest test-build-url
(is (= (s/build-url [["customers" "cards"] [:customer_id :card_id]]
{:customer_id "PHL123" :card_id "ORD789"})
(str s/stripe-api-url "customers/PHL123/cards/ORD789")) "build normal url")
(is (= (s/build-url [["charges" "refund"] [:charge_id nil]]
{:customer_id "LAX123" :charge_id "JFK789"})
(str s/stripe-api-url "charges/JFK789/refund")) "build nil-value url"))

(deftest test-url-mapping
(is (= (:cards s/url-mapping) [["customers" "cards"] [:customer_id :card_id]]) "test url mapping"))

(def card {:number "4242424242424242"
:exp_month 12
:exp_year 2020
:cvc 123
:name "Mr. Stripe Clojure"})
(def customer {:card card
:email "mrclojure@stripetest.com"
:description "customer test from clj-stripe"})

(s/set-tokens! secret-tokens)
(deftest functional-test
(testing "Functional Test -"
(let [{id :id :as c} (s/create {:customers customer})
c-id {:customer_id id}]
(testing "create" (is (not (nil? (:id c)))))
(testing "retrieve" (is (= (s/retrieve {:customers c-id}) c)))
(testing "update" (is (= (s/update {:customers
(merge c-id {:email "newmrclojure@stripetest.com"})})
(s/retrieve {:customers c-id}))))
(s/delete {:customers c-id})
(testing "delete and list" (is (every? false?
(map #(= id (:id %))
(:data (s/list {:customers {}})))))))))

(deftest error-message
(is (contains? (s/create {:customers {:customer_id "xxx1" :fail "test"}}) :error) "error message return"))
3 changes: 3 additions & 0 deletions test/stripe_clojure/test/core_config.clj.example
@@ -0,0 +1,3 @@
(ns stripe-clojure.test.core-config)

(def secret-tokens {:private ""})

0 comments on commit dca3ce1

Please sign in to comment.