Permalink
Browse files

1.0.0

  • Loading branch information...
1 parent d1c6a3c commit f601a0292b1894604bddd8f2252e3120c7feb6cd @braintreeps braintreeps committed Feb 10, 2012
Showing with 4,458 additions and 2,572 deletions.
  1. +4 −0 CHANGELOG.md
  2. +0 −9 README.md
  3. +18 −5 Rakefile
  4. +1 −1 lib/braintree.coffee
  5. +112 −0 lib/braintree/advanced_search.coffee
  6. +23 −0 lib/braintree/credit_card.coffee
  7. +12 −0 lib/braintree/credit_card_gateway.coffee
  8. +2 −1 lib/braintree/customer.coffee
  9. +9 −0 lib/braintree/customer_gateway.coffee
  10. +30 −0 lib/braintree/customer_search.coffee
  11. +11 −1 lib/braintree/gateway.coffee
  12. +26 −0 lib/braintree/search_response.coffee
  13. +12 −0 lib/braintree/subscription.coffee
  14. +7 −0 lib/braintree/subscription_gateway.coffee
  15. +13 −0 lib/braintree/subscription_search.coffee
  16. +32 −0 lib/braintree/transaction.coffee
  17. +7 −0 lib/braintree/transaction_gateway.coffee
  18. +76 −0 lib/braintree/transaction_search.coffee
  19. +31 −0 lib/braintree/util.coffee
  20. +1 −1 package.json
  21. +0 −253 spec/braintree/address_gateway_spec.js
  22. +0 −23 spec/braintree/braintree_spec.js
  23. +0 −347 spec/braintree/credit_card_gateway_spec.js
  24. +0 −636 spec/braintree/customer_gateway_spec.js
  25. +0 −20 spec/braintree/digest_spec.js
  26. +0 −60 spec/braintree/http_spec.js
  27. +0 −111 spec/braintree/settlement_batch_summary_gateway_spec.js
  28. +0 −600 spec/braintree/transaction_gateway_spec.js
  29. +0 −316 spec/braintree/transparent_redirect_gateway_spec.js
  30. +0 −167 spec/braintree/util_spec.js
  31. +0 −21 spec/braintree/validation_errors_collection_spec.js
  32. +215 −0 spec/integration/braintree/address_gateway_spec.coffee
  33. +788 −0 spec/integration/braintree/advanced_search_spec.coffee
  34. +22 −0 spec/integration/braintree/braintree_spec.coffee
  35. +301 −0 spec/integration/braintree/credit_card_gateway_spec.coffee
  36. +525 −0 spec/integration/braintree/customer_gateway_spec.coffee
  37. +196 −0 spec/integration/braintree/customer_search_spec.coffee
  38. +53 −0 spec/integration/braintree/http_spec.coffee
  39. +81 −0 spec/integration/braintree/settlement_batch_summary_gateway_spec.coffee
  40. +333 −0 spec/integration/braintree/subscription_gateway_spec.coffee
  41. +96 −0 spec/integration/braintree/subscription_search_spec.coffee
  42. +515 −0 spec/integration/braintree/transaction_gateway_spec.coffee
  43. +185 −0 spec/integration/braintree/transaction_search_spec.coffee
  44. +244 −0 spec/integration/braintree/transparent_redirect_gateway_spec.coffee
  45. +8 −0 spec/spec_helper.coffee
  46. +111 −0 spec/unit/braintree/advanced_search_spec.coffee
  47. +18 −0 spec/unit/braintree/digest_spec.coffee
  48. +61 −0 spec/unit/braintree/search_response_spec.coffee
  49. +259 −0 spec/unit/braintree/util_spec.coffee
  50. +20 −0 spec/unit/braintree/validation_errors_collection_spec.coffee
View
@@ -1,3 +1,7 @@
+## 1.0.0
+
+* Add search functionality
+
## 0.5.1
* Exposes plan_id on transactions
View
@@ -2,11 +2,6 @@
This is a Node.js library for integrating with the [Braintree](http://www.braintreepayments.com) gateway.
-The library is a work in progress and a few features are still missing. Until
-we hit version 1.0 we may break backwards compatibility, but the changes
-should be minimal. We're using [semantic versioning](http://semver.org/).
-[Email us](mailto:support@braintreepayments.com) if you have any questions.
-
## Installing
### From NPM
@@ -25,10 +20,6 @@ should be minimal. We're using [semantic versioning](http://semver.org/).
* coffee-script ~1.1
* xml2js >= 0.1.13
-## Not Yet Implemented
-
-* search APIs (transactions, vault, subscriptions, expired cards)
-
## Links
* [Documentation](http://www.braintreepayments.com/docs/node)
View
@@ -1,8 +1,21 @@
-task :default => :spec
+task :default => %w[spec:unit spec:integration]
-desc "run the specs"
-task :spec do
- local_vows = "./node_modules/.bin/vows"
+namespace :spec do
+ desc "Run units"
+ task :unit => :install_vows do
+ sh "#{local_vows} " + Dir.glob("spec/unit/**/*_spec.coffee").join(" ")
+ end
+
+ desc "Run integration"
+ task :integration => :install_vows do
+ sh "#{local_vows} " + Dir.glob("spec/integration/**/*_spec.coffee").join(" ")
+ end
+end
+
+task :install_vows do
sh "npm install" unless File.exist?(local_vows)
- sh "#{local_vows} " + Dir.glob("spec/**/*_spec.js").join(" ")
+end
+
+def local_vows
+ "./node_modules/.bin/vows"
end
View
@@ -7,6 +7,6 @@ connect = (config) ->
new BraintreeGateway(new Config(config))
exports.connect = connect
-exports.version = '1.0.105'
+exports.version = '1.0.0'
exports.Environment = Environment
exports.errorTypes = errorTypes
@@ -0,0 +1,112 @@
+{Util} = require('./util')
+
+class AdvancedSearch
+ @equalityFields: (fields...) ->
+ @_createFieldAccessors(fields, EqualityNode)
+
+ @partialMatchFields: (fields...) ->
+ @_createFieldAccessors(fields, PartialMatchNode)
+
+ @textFields: (fields...) ->
+ @_createFieldAccessors(fields, TextNode)
+
+ @keyValueFields: (fields...) ->
+ @_createFieldAccessors(fields, KeyValueNode)
+
+ @multipleValueField: (field, options = {}) ->
+ @_createFieldAccessors([field], MultipleValueNode, options)
+
+ @multipleValueOrTextField: (field, options = {}) ->
+ @_createFieldAccessors([field], MultipleValueOrTextNode, options)
+
+ @rangeFields: (fields...) ->
+ @_createFieldAccessors(fields, RangeNode)
+
+ @_createFieldAccessors: (fields, nodeClass, options) ->
+ @::[field] = @_fieldTemplate(field, nodeClass, options) for field in fields
+
+ @_fieldTemplate: (field, nodeClass, options) ->
+ -> new nodeClass(field, @, options)
+
+ constructor: -> @criteria = {}
+
+ addCriteria: (key, value) ->
+ if @criteria[key] is Object(@criteria[key])
+ Util.merge(@criteria[key], value)
+ else
+ @criteria[key] = value
+
+ toHash: -> @criteria
+
+class SearchNode
+ @operators: (operators...) ->
+ operatorTemplate = (operator) =>
+ (value) ->
+ criterion = {}
+ criterion[operator] = "#{value}"
+ @parent.addCriteria(@nodeName, criterion)
+
+ @::[operator] = operatorTemplate(operator) for operator in operators
+
+ constructor: (nodeName, parent) ->
+ @nodeName = nodeName
+ @parent = parent
+
+class EqualityNode extends SearchNode
+ @operators("is", "isNot")
+
+class PartialMatchNode extends EqualityNode
+ @operators("endsWith", "startsWith")
+
+class TextNode extends PartialMatchNode
+ @operators("contains")
+
+class KeyValueNode extends SearchNode
+ is: (value) -> @parent.addCriteria(@nodeName, value)
+
+class MultipleValueNode extends SearchNode
+ constructor: (nodeName, parent, options) ->
+ super(nodeName, parent)
+ @options = options
+
+ allowedValues: -> @options['allows']
+
+ in: (values...) ->
+ values = Util.flatten(values)
+
+ if @allowedValues?()
+ allowedValues = @allowedValues()
+ badValues = Util.without(values, allowedValues)
+ throw new Error("Invalid argument(s) for #{@nodeName}") unless Util.arrayIsEmpty(badValues)
+
+ @parent.addCriteria(@nodeName, values)
+
+ is: (value) -> @in(value)
+
+class MultipleValueOrTextNode extends MultipleValueNode
+ @delegators: (delegatedMethods...) ->
+ delegatorTemplate = (methodName) =>
+ (value) -> @textNode[methodName](value)
+
+ @::[methodName] = delegatorTemplate(methodName) for methodName in delegatedMethods
+
+ @delegators("contains", "endsWith", "is", "isNot", "startsWith")
+
+ constructor: (nodeName, parent, options) ->
+ super
+ @textNode = new TextNode(nodeName, parent)
+
+class RangeNode extends SearchNode
+ @operators("is")
+
+ between: (min, max) ->
+ @min(min)
+ @max(max)
+
+ max: (value) ->
+ @parent.addCriteria(@nodeName, { max : value})
+
+ min: (value) ->
+ @parent.addCriteria(@nodeName, { min : value})
+
+exports.AdvancedSearch = AdvancedSearch
@@ -1,6 +1,29 @@
{AttributeSetter} = require('./attribute_setter')
class CreditCard extends AttributeSetter
+ @CardType =
+ AmEx : "American Express"
+ CarteBlanche : "Carte Blanche"
+ ChinaUnionPay : "China UnionPay"
+ DinersClubInternational : "Diners Club"
+ Discover : "Discover"
+ JCB : "JCB"
+ Laser : "Laser"
+ Maestro : "Maestro"
+ MasterCard : "MasterCard"
+ Solo : "Solo"
+ Switch : "Switch"
+ Visa : "Visa"
+ Unknown : "Unknown"
+ All : ->
+ all = []
+ for key, value of @
+ all.push value if key isnt 'All'
+ all
+ @CustomerLocation =
+ International : 'international'
+ US : 'us'
+
constructor: (attributes) ->
super attributes
@maskedNumber = "#{@bin}******#{@last4}"
@@ -27,4 +27,16 @@ class CreditCardGateway extends Gateway
responseHandler: (callback) ->
@createResponseHandler("creditCard", CreditCard, callback)
+ expired: (callback) ->
+ @gateway.http.post("/payment_methods/all/expired_ids", {}, @searchResponseHandler(@, callback))
+
+ expiringBetween: (after, before, callback) ->
+ url = "/payment_methods/all/expiring_ids?start=#{@dateFormat(after)}&end=#{@dateFormat(before)}"
+ @gateway.http.post(url, {}, @searchResponseHandler(@, callback))
+
+ dateFormat: (date) ->
+ month = date.getMonth() + 1
+ month = "0#{month}" if month < 10
+ return month + date.getFullYear()
+
exports.CreditCardGateway = CreditCardGateway
@@ -4,6 +4,7 @@
class Customer extends AttributeSetter
constructor: (attributes) ->
super attributes
- @creditCards = (new CreditCard(cardAttributes) for cardAttributes in attributes.creditCards)
+ if attributes.creditCards
+ @creditCards = (new CreditCard(cardAttributes) for cardAttributes in attributes.creditCards)
exports.Customer = Customer
@@ -1,5 +1,8 @@
{Gateway} = require('./gateway')
{Customer} = require('./customer')
+{CustomerSearch} = require('./customer_search')
+sys = require('sys')
+
exceptions = require('./exceptions')
class CustomerGateway extends Gateway
@@ -24,7 +27,13 @@ class CustomerGateway extends Gateway
update: (customerId, attributes, callback) ->
@gateway.http.put("/customers/#{customerId}", {customer: attributes}, @responseHandler(callback))
+ search: (fn, callback) ->
+ search = new CustomerSearch()
+ fn(search)
+ @gateway.http.post("/customers/advanced_search_ids", {search: search.toHash()}, @searchResponseHandler(@, callback))
+
responseHandler: (callback) ->
@createResponseHandler("customer", Customer, callback)
+
exports.CustomerGateway = CustomerGateway
@@ -0,0 +1,30 @@
+{AdvancedSearch} = require('./advanced_search')
+
+class CustomerSearch extends AdvancedSearch
+ @textFields(
+ "addressCountryName"
+ "addressExtendedAddress"
+ "addressFirstName"
+ "addressLastName"
+ "addressLocality"
+ "addressPostalCode"
+ "addressRegion"
+ "addressStreetAddress"
+ "cardholderName"
+ "company"
+ "email"
+ "fax"
+ "firstName"
+ "id"
+ "lastName"
+ "paymentMethodToken"
+ "phone"
+ "website"
+ )
+
+ @equalityFields "creditCardExpirationDate"
+ @partialMatchFields "creditCardNumber"
+ @multipleValueField "ids"
+ @rangeFields "createdAt"
+
+exports.CustomerSearch = CustomerSearch
@@ -1,4 +1,5 @@
{ErrorResponse} = require('./error_response')
+{SearchResponse} = require ('./search_response')
class Gateway
createResponseHandler: (attributeName, klass, callback) ->
@@ -7,9 +8,18 @@ class Gateway
if (response[attributeName])
response.success = true
- response[attributeName] = new klass(response[attributeName])
+ response[attributeName] = new klass(response[attributeName]) if klass?
callback(null, response)
else if (response.apiErrorResponse)
callback(null, new ErrorResponse(response.apiErrorResponse))
+ searchResponseHandler: (gateway, callback) ->
+ (err, response) ->
+ return callback(err, response) if err
+ if (response["searchResults"])
+ container = new SearchResponse(gateway, response)
+ callback(null, container)
+ else if (response.apiErrorResponse)
+ callback(null, new ErrorResponse(response.apiErrorResponse))
+
exports.Gateway = Gateway
@@ -0,0 +1,26 @@
+class SearchResponse
+ constructor: (gateway, results) ->
+ @ids = results.searchResults.ids
+ @gateway = gateway
+ @success = true
+
+ first: (callback)->
+ if @ids.length == 0
+ callback(null, null)
+ else
+ @gateway.find(@ids[0], callback)
+
+ next: (resultsLeft, callback) ->
+ if resultsLeft.length > 0
+ @gateway.find(resultsLeft[0], (err, result) =>
+ callback(err, result)
+ @next(resultsLeft.slice(1), callback)
+ )
+
+ each: (callback)->
+ @next(@ids, callback)
+
+ length: ->
+ @ids.length
+
+exports.SearchResponse = SearchResponse
@@ -2,6 +2,18 @@
{Transaction} = require('./transaction')
class Subscription extends AttributeSetter
+ @Status =
+ Active : 'Active'
+ Canceled : 'Canceled'
+ Expired : 'Expired'
+ PastDue : 'Past Due'
+ Pending : 'Pending'
+ All : ->
+ all = []
+ for key, value of @
+ all.push value if key isnt 'All'
+ all
+
constructor: (attributes) ->
super attributes
@transactions = (new Transaction(transactionAttributes) for transactionAttributes in attributes.transactions)
@@ -1,5 +1,6 @@
{Gateway} = require('./gateway')
{Subscription} = require('./subscription')
+{SubscriptionSearch} = require('./subscription_search')
{TransactionGateway} = require('./transaction_gateway')
exceptions = require('./exceptions')
@@ -31,6 +32,12 @@ class SubscriptionGateway extends Gateway
subscriptionId: subscriptionId
, callback
+ search: (fn, callback) ->
+ search = new SubscriptionSearch()
+ fn(search)
+ @gateway.http.post("/subscriptions/advanced_search_ids",
+ { search : search.toHash() }, @searchResponseHandler(@, callback))
+
update: (subscriptionId, attributes, callback) ->
@gateway.http.put("/subscriptions/#{subscriptionId}", {subscription: attributes}, @responseHandler(callback))
@@ -0,0 +1,13 @@
+{AdvancedSearch} = require('./advanced_search')
+{Subscription} = require('./subscription')
+
+class SubscriptionSearch extends AdvancedSearch
+ @multipleValueField "inTrialPeriod"
+ @multipleValueField "ids"
+ @textFields "id", "transactionId"
+ @multipleValueOrTextField "planId"
+ @multipleValueField "status", { "allows" : Subscription.Status.All() }
+ @multipleValueField "merchantAccountId"
+ @rangeFields "price", "daysPastDue", "billingCyclesRemaining", "nextBillingDate"
+
+exports.SubscriptionSearch = SubscriptionSearch
Oops, something went wrong.

0 comments on commit f601a02

Please sign in to comment.