Permalink
Browse files

2.3.0

  • Loading branch information...
1 parent b651428 commit eeab75d3ba59d703cc34e62103be8c7382c93bc9 @braintreeps braintreeps committed Sep 13, 2017
Showing with 1,547 additions and 83 deletions.
  1. +2 −0 .gitignore
  2. +11 −0 CHANGELOG.md
  3. +2 −2 Rakefile
  4. +7 −0 lib/braintree/authorization_adjustment.js
  5. +4 −0 lib/braintree/braintree_gateway.js
  6. +4 −1 lib/braintree/dispute.js
  7. +153 −0 lib/braintree/dispute_gateway.js
  8. +29 −0 lib/braintree/dispute_search.js
  9. +14 −0 lib/braintree/document_upload.js
  10. +47 −0 lib/braintree/document_upload_gateway.js
  11. +11 −0 lib/braintree/facilitated_details.js
  12. +82 −4 lib/braintree/http.js
  13. +2 −2 lib/braintree/paginated_response.js
  14. +15 −4 lib/braintree/paginated_response_stream.js
  15. +9 −2 lib/braintree/subscription_gateway.js
  16. +4 −0 lib/braintree/test/credit_card_numbers.js
  17. +4 −0 lib/braintree/transaction.js
  18. +17 −1 lib/braintree/validation_error_codes.js
  19. +0 −9 lib/braintree/webhook_testing_gateway.js
  20. +3 −3 package.json
  21. BIN spec/fixtures/bt_logo.png
  22. BIN spec/fixtures/gif_extension_bt_logo.gif
  23. BIN spec/fixtures/malformed_pdf.pdf
  24. +3 −2 spec/integration/braintree/credit_card_gateway_spec.js
  25. +8 −3 spec/integration/braintree/customer_gateway_spec.js
  26. +336 −0 spec/integration/braintree/dispute_gateway_spec.js
  27. +125 −0 spec/integration/braintree/dispute_search_spec.js
  28. +133 −0 spec/integration/braintree/document_upload_gateway_spec.js
  29. +58 −40 spec/integration/braintree/payment_method_gateway_spec.js
  30. +84 −0 spec/integration/braintree/subscription_gateway_spec.js
  31. +32 −10 spec/integration/braintree/transaction_gateway_spec.js
  32. +187 −0 spec/unit/braintree/dispute_gateway_spec.js
  33. +161 −0 spec/unit/braintree/dispute_spec.js
View
@@ -1,3 +1,5 @@
node_modules
spec_compiled
tags
+npm-debug.log
+large_file.png
View
@@ -1,3 +1,14 @@
+## 2.3.0
+* Add AuthorizationAdjustment class and `authorizationAdjustments` to Transaction
+* Coinbase is no longer a supported payment method. `PaymentMethodNoLongerSupported` will be returned for Coinbase operations.
+* Add facilitated details to Transaction if present
+* Add `submit_for_settlement` to `Subscription.retryCharge`
+* Add `options` -> `paypal` -> `description` for creating and updating subscriptions
+* Add Dispute API
+* Add DocumentUpload API
+* Add `deviceDataCaptured` field in `riskData`
+* Add support for upgrading a PayPal future payment refresh token to a billing agreement
+
## 2.2.0
* Fixes wrap-promise issue (closes #102) - Thanks @targunp
* Add iDEAL to PaymentInstrumentTypes
View
@@ -19,7 +19,7 @@ namespace :test do
command = local_mocha
if filename.include? "integration"
- command += "--slow 2000"
+ command += " --slow 2000"
end
sh "#{command} #{filename}"
end
@@ -30,5 +30,5 @@ task :npm_install do
end
def local_mocha
- "./node_modules/mocha/bin/mocha --timeout 62000 --reporter spec"
+ "./node_modules/mocha/bin/mocha --timeout 62000 --reporter spec -r spec/spec_helper"
end
@@ -0,0 +1,7 @@
+'use strict';
+
+let AttributeSetter = require('./attribute_setter').AttributeSetter;
+
+class AuthorizationAdjustment extends AttributeSetter {}
+
+module.exports = {AuthorizationAdjustment: AuthorizationAdjustment};
@@ -9,6 +9,8 @@ let CreditCardVerificationGateway = require('./credit_card_verification_gateway'
let CustomerGateway = require('./customer_gateway').CustomerGateway;
let DisbursementGateway = require('./disbursement_gateway').DisbursementGateway;
let DiscountGateway = require('./discount_gateway').DiscountGateway;
+let DisputeGateway = require('./dispute_gateway').DisputeGateway;
+let DocumentUploadGateway = require('./document_upload_gateway').DocumentUploadGateway;
let MerchantAccountGateway = require('./merchant_account_gateway').MerchantAccountGateway;
let MerchantGateway = require('./merchant_gateway').MerchantGateway;
let OAuthGateway = require('./oauth_gateway').OAuthGateway;
@@ -38,6 +40,8 @@ class BraintreeGateway {
this.customer = new CustomerGateway(this);
this.disbursement = new DisbursementGateway(this);
this.discount = new DiscountGateway(this);
+ this.dispute = new DisputeGateway(this);
+ this.documentUpload = new DocumentUploadGateway(this);
this.merchantAccount = new MerchantAccountGateway(this);
this.merchant = new MerchantGateway(this);
this.oauth = new OAuthGateway(this);
View
@@ -8,7 +8,10 @@ class Dispute extends AttributeSetter {
this.Status = {
Open: 'open',
Lost: 'lost',
- Won: 'won'
+ Won: 'won',
+ Accepted: 'accepted',
+ Disputed: 'disputed',
+ Expired: 'expired'
};
this.Reason = {
CancelledRecurringTransaction: 'cancelled_recurring_transaction',
@@ -0,0 +1,153 @@
+'use strict';
+
+let _ = require('underscore');
+let Gateway = require('./gateway').Gateway;
+let Dispute = require('./dispute').Dispute;
+let DisputeSearch = require('./dispute_search').DisputeSearch;
+let InvalidKeysError = require('./exceptions').InvalidKeysError;
+let NotFoundError = require('./exceptions').NotFoundError;
+let PaginatedResponse = require('./paginated_response').PaginatedResponse;
+let wrapPrototype = require('@braintree/wrap-promise').wrapPrototype;
+
+class DisputeGateway extends Gateway {
+ constructor(gateway) {
+ super();
+ this.gateway = gateway;
+ this.config = this.gateway.config;
+ }
+
+ accept(id) {
+ let notFoundError = new NotFoundError("dispute with id '" + id + "' not found");
+
+ if (id == null || id.trim() === '') {
+ return Promise.reject(notFoundError);
+ }
+
+ return this.gateway.http.put(`${this.config.baseMerchantPath()}/disputes/${id}/accept`)
+ .then(this.createResponseHandler('dispute', Dispute))
+ .catch(this.createRejectionHandler(notFoundError));
+ }
+
+ addTextEvidence(id, content) {
+ let notFoundError = new NotFoundError("dispute with id '" + id + "' not found");
+
+ if (id == null || id.trim() === '') {
+ return Promise.reject(notFoundError);
+ }
+
+ if (content == null || content.trim() === '') {
+ return Promise.reject(new InvalidKeysError('content cannot be null'));
+ }
+
+ return this.gateway.http.post(`${this.config.baseMerchantPath()}/disputes/${id}/evidence`, {
+ comments: content
+ })
+ .then(this.createResponseHandler())
+ .catch(this.createRejectionHandler(notFoundError));
+ }
+
+ addFileEvidence(disputeId, documentId) {
+ let notFoundError = new NotFoundError("dispute with id '" + disputeId + "' not found");
+
+ if (disputeId == null || disputeId.trim() === '') {
+ return Promise.reject(notFoundError);
+ }
+
+ if (documentId == null || documentId.trim() === '') {
+ return Promise.reject(new NotFoundError("document with id '" + documentId + "' not found"));
+ }
+
+ return this.gateway.http.post(`${this.config.baseMerchantPath()}/disputes/${disputeId}/evidence`, {
+ document_upload_id: documentId // eslint-disable-line camelcase
+ })
+ .then(this.createResponseHandler())
+ .catch(this.createRejectionHandler(notFoundError));
+ }
+
+ finalize(id) {
+ let notFoundError = new NotFoundError(`dispute with id '${id}' not found`);
+
+ if (id == null || id.trim() === '') {
+ return Promise.reject(notFoundError);
+ }
+
+ return this.gateway.http.put(`${this.config.baseMerchantPath()}/disputes/${id}/finalize`)
+ .then(this.createResponseHandler())
+ .catch(this.createRejectionHandler(notFoundError));
+ }
+
+ find(id) {
+ let notFoundError = new NotFoundError(`dispute with id '${id}' not found`);
+
+ if (id == null || id.trim() === '') {
+ return Promise.reject(notFoundError);
+ }
+
+ return this.gateway.http.get(`${this.config.baseMerchantPath()}/disputes/${id}`)
+ .then(this.createResponseHandler('dispute', Dispute))
+ .catch(this.createRejectionHandler(notFoundError));
+ }
+
+ removeEvidence(disputeId, evidenceId) {
+ let notFoundError = new NotFoundError("evidence with id '" + evidenceId + "' for dispute with id '" + disputeId + "' not found");
+
+ if (disputeId == null || disputeId.trim() === '' || evidenceId == null || evidenceId.trim() === '') {
+ return Promise.reject(notFoundError);
+ }
+
+ return this.gateway.http.delete(`${this.config.baseMerchantPath()}/disputes/${disputeId}/evidence/${evidenceId}`)
+ .then(this.createResponseHandler())
+ .catch(this.createRejectionHandler(notFoundError));
+ }
+
+ search(searchFunction, callback) {
+ let search = new DisputeSearch();
+
+ searchFunction(search);
+
+ let response = new PaginatedResponse(this.fetchDisputes.bind(this), {
+ search: search.toHash()
+ });
+
+ if (callback != null) {
+ return response.all(callback);
+ }
+
+ response.ready();
+ return response.stream;
+ }
+
+ fetchDisputes(pageNumber, search, callback) {
+ return this.gateway.http.post(`${this.config.baseMerchantPath()}/disputes/advanced_search?page=${pageNumber}`, {search: search}, (err, response) => {
+ if (err) {
+ return callback(err);
+ }
+
+ let totalItems = response.disputes.totalItems;
+ let pageSize = response.disputes.pageSize;
+ let disputes = response.disputes.dispute;
+
+ if (!disputes) {
+ disputes = [null];
+ } else if (!_.isArray(disputes)) {
+ disputes = [disputes];
+ }
+
+ return callback(null, totalItems, pageSize, disputes);
+ });
+ }
+
+ createRejectionHandler(notFoundError) {
+ return function (err) {
+ if (err.type === 'notFoundError') {
+ err = notFoundError;
+ }
+
+ return Promise.reject(err);
+ };
+ }
+}
+
+module.exports = {DisputeGateway: wrapPrototype(DisputeGateway, {
+ ignoreMethods: ['search', 'fetchDisputes']
+})};
@@ -0,0 +1,29 @@
+'use strict';
+
+let AdvancedSearch = require('./advanced_search').AdvancedSearch;
+
+class DisputeSearch extends AdvancedSearch {
+ static initClass() {
+ this.textFields(
+ 'caseNumber',
+ 'id',
+ 'referenceNumber',
+ 'transactionId'
+ );
+ this.multipleValueField('kind');
+ this.multipleValueField('merchantAccountId');
+ this.multipleValueField('reason');
+ this.multipleValueField('reasonCode');
+ this.multipleValueField('status');
+ this.multipleValueField('transactionSource');
+ this.rangeFields(
+ 'amountDisputed',
+ 'amountWon',
+ 'receivedDate',
+ 'replyByDate'
+ );
+ }
+}
+DisputeSearch.initClass();
+
+module.exports = {DisputeSearch: DisputeSearch};
@@ -0,0 +1,14 @@
+'use strict';
+
+let AttributeSetter = require('./attribute_setter').AttributeSetter;
+
+class DocumentUpload extends AttributeSetter {
+ static initClass() {
+ this.Kind = {
+ EvidenceDocument: 'evidence_document'
+ };
+ }
+}
+DocumentUpload.initClass();
+
+module.exports = {DocumentUpload: DocumentUpload};
@@ -0,0 +1,47 @@
+'use strict';
+
+let DocumentUpload = require('./document_upload').DocumentUpload;
+let Gateway = require('./gateway').Gateway;
+let InvalidKeysError = require('./exceptions').InvalidKeysError;
+let Readable = require('stream').Readable;
+let Util = require('./util').Util;
+let wrapPrototype = require('@braintree/wrap-promise').wrapPrototype;
+
+class DocumentUploadGateway extends Gateway {
+ constructor(gateway) {
+ super();
+ this.gateway = gateway;
+ this.config = this.gateway.config;
+ }
+
+ create(options) {
+ if (!options.file || !(options.file instanceof Readable)) {
+ return Promise.reject(new InvalidKeysError('file must be a Readable stream'));
+ }
+
+ let params = {
+ file: {
+ path: options.file.path
+ },
+ kind: options.kind
+ };
+ let invalidKeysError = Util.verifyKeys(this._createSignature(), params);
+
+ if (invalidKeysError) {
+ return Promise.reject(invalidKeysError);
+ }
+
+ return this.gateway.http.postMultipart(`${this.config.baseMerchantPath()}/document_uploads`, {
+ 'document_upload[kind]': params.kind
+ }, params.file)
+ .then(this.createResponseHandler('documentUpload', DocumentUpload));
+ }
+
+ _createSignature() {
+ return {
+ valid: ['kind', 'file[path]']
+ };
+ }
+}
+
+module.exports = {DocumentUploadGateway: wrapPrototype(DocumentUploadGateway)};
@@ -0,0 +1,11 @@
+'use strict';
+
+let AttributeSetter = require('./attribute_setter').AttributeSetter;
+
+class FacilitatedDetails extends AttributeSetter {
+ constructor(attributes) {
+ super(attributes);
+ }
+}
+
+module.exports = {FacilitatedDetails: FacilitatedDetails};
Oops, something went wrong.

0 comments on commit eeab75d

Please sign in to comment.