diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8029c6d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +testbox/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d46d39c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 093df8b..2c27915 100644 --- a/README.md +++ b/README.md @@ -1,197 +1,116 @@ -stripe.cfc -================= - -**stripe.cfc** is a(nother) Railo/ColdFusion wrapper for the stripe.com API. - -It was tested against and supports all of the routes of the **2015-03-24** version of the stripe.com API. It should run on Railo 3.3+ and ColdFusion 9.0.1+. - -Getting Started -================= - - // To charge $20 to a card for which a card token has been created - stripe = new path.to.stripe( 'STRIPE_SECRET_KEY' ); - charge = stripe.createCharge( amount = 2000, source = cardToken ); - // charge is a struct which can be inspected for the result of the create charge api call - writeDump( charge ); - -Changelog -================= - -#### Updated to **2015-03-24** version of the stripe.com API - -- **IMPORTANT** `card` and `default_card` have been replaced with `source` and `default_source`. This means that in the `createCharge` method, for instance, the `card` argument has been renamed to `source`. Correspondingly, the `CustomerCard` methods have been replaced with `CustomerSource` methods. -- In addition to the `apiKey` argument mentioned below, there are now two other arguments that can be passed to any method: `stripeAccount` and `idempotencyKey`. When either argument is included stripe.cfc will add a `Stripe-Account` or `Idempotency-Key` header to the api request respectively. -- The `recipients` api endpoint has been deprecated. - -#### Updated to **2014-03-28** version of the stripe.com API - -- Although not listed in any method's arguments, it is now possible to pass in an `apiKey` argument containing a stripe secret key or an access token (for use with Stripe Connect) to any method, and stripe.cfc will use that api key for the api request instead of the default api key passed in when stripe.cfc was initialized. -- Method arguments have been reordered, with ids first, followed by required arguments in alphabetical order, and then optional arguments in alphabetical order. It is recommended to pass arguments into methods by name, to avoid running into issues caused by the reordering, removal, or addition of arguments. -- Pagination has undergone a significant change. The `count` and `offset` arguments are gone. They have been replaced by `limit`, `starting_after`, and `ending_before`. Both `starting_after` and `ending_before` take a stripe object ID as a value. See https://stripe.com/docs/api#pagination. While not listed in any lists() methods, stripe.cfc looks for an `include` array argument that can be used to get the total count of records when using a list method by passing in `include = [ "total_count" ]` to the method. -- It should also be mentioned that there is a similar `expand` array argument that can be used to expand certain properties of the stripe API response so that it includes more detail. See https://stripe.com/docs/api#expand. - -More Details about stripe.cfc -================= - -By default, stripe.cfc converts all UTC timestamps to and from CFML date variables, but this can be disabled by passing in `convertUTCTimestamps = false` when stripe.cfc is initialized. - -To use dollars instead of cents, pass `convertToCents = true` into the init() method when stripe.cfc is created. Stripe.cfc will then multiply all input currency amounts by 100, and divide all return currency amounts by 100. For example: - - stripe = new path.to.stripe( stripeSecretKey = 'STRIPE_SECRET_KEY', convertToCents = true ); - charge = stripe.createCharge( amount = 19.99, card = cardToken ); - // stripe.cfc will convert the 19.99 to 1999 before calling the API - -The default currency can also be set at init: `defaultCurrency = "usd"`, as well as overridden on a method by method basis. - -**What is Returned** - -The stripe.com API returns a JSON object in response to all api requests (see https://stripe.com/docs/api). Stripe.cfc deserializes this object into a CFML struct. It also adds three key-value pairs to this struct: 'api_request_time', 'status_code', and 'status_text'. 'api_request_time' is the total time in milliseconds for the HTTP request. 'status_code' and 'status_text' refer to the status code received by the HTTP request. If '200 OK' is received, then 'status_code' will be '200' and 'status_text' will be 'OK'. - -Stripe.cfc by default converts all UTC timestamps in the returned JSON object to CFML dates. - -Available Methods -================= -**Charges** https://stripe.com/docs/api#charges - - createCharge( required numeric amount, string currency = variables.defaultCurrency, numeric application_fee, boolean capture, string customer, string description, string destination, struct metadata, string receipt_email, struct shipping, any source, string statement_descriptor ) - getCharge( required string id ) - updateCharge( required string id, string description, struct metadata ) - captureCharge( required string id, numeric amount, numeric application_fee ) - listCharges( any created, string customer, string ending_before, numeric limit, string starting_after ) - -**Refunds** https://stripe.com/docs/api#refunds - - createChargeRefund( required string id, numeric amount, struct metadata, string reason, boolean refund_application_fee ) - getChargeRefund( required string charge_id, required string id ) - updateChargeRefund( required string charge_id, required string id, struct metadata ) - listChargeRefunds( required string charge_id, string ending_before, numeric limit, string starting_after ) - -**Customers** https://stripe.com/docs/api#customers - - createCustomer( numeric account_balance, string coupon, string description, string email, struct metadata, string plan, numeric quantity, any source, any trial_end ) - getCustomer( required string id ) - updateCustomer( required string id, numeric account_balance, string coupon, string default_source, string description, string email, struct metadata, any source ) - deleteCustomer( required string id ) - listCustomers( any created, string ending_before, numeric limit, string starting_after ) - -**Cards** https://stripe.com/docs/api#cards - - createCustomerSource( required string customer_id, any source ) - getCustomerSource( required string customer_id, required string id ) - updateCustomerSource( required string customer_id, required string id, string address_city, string address_country, string address_line1, string address_line2, string address_state, string address_zip, numeric exp_month, numeric exp_year, struct metadata, string name ) - deleteCustomerSource( required string customer_id, required string id ) - listCustomerSources( required string customer_id, string ending_before, numeric limit, string starting_after ) - -**Subscriptions** https://stripe.com/docs/api#subscriptions - - createCustomerSubscription( required string customer_id, required string plan, numeric application_fee_percent, string coupon, struct metadata, boolean prorate, numeric quantity, any source, numeric tax_percent, any trial_end ) - getCustomerSubscription( required string customer_id, required string id ) - updateCustomerSubscription( required string customer_id, required string id, numeric application_fee_percent, string coupon, struct metadata, string plan, boolean prorate, numeric quantity, any source, numeric tax_percent, any trial_end ) - cancelCustomerSubscription( required string customer_id, required string id, boolean at_period_end ) - listCustomerSubscriptions( required string customer_id, string ending_before, numeric limit, string starting_after ) - -**Plans** https://stripe.com/docs/api#plans - - createPlan( required string id, required numeric amount, string currency = variables.defaultCurrency, required string interval, required string name, numeric interval_count, struct metadata, string statement_descriptor, numeric trial_period_days ) - getPlan( required string id ) - updatePlan( required string id, struct metadata, string name, string statement_descriptor ) - deletePlan( required string id ) - listPlans( any created, string ending_before, numeric limit, string starting_after ) - -**Coupons** https://stripe.com/docs/api#coupons - - createCoupon( required string duration, numeric amount_off, string currency = variables.defaultCurrency, numeric duration_in_months, string id, numeric max_redemptions, struct metadata, numeric percent_off, any redeem_by ) - getCoupon( required string id ) - updateCoupon( required string id, struct metadata ) - deleteCoupon( required string id ) - listCoupons( any created, string ending_before, numeric limit, string starting_after ) - -**Discounts** https://stripe.com/docs/api#discounts - - deleteCustomerDiscount( required string id ) - deleteCustomerSubscriptionDiscount( required string customer_id, required string id ) - -**Invoices** https://stripe.com/docs/api#invoices - - createInvoice( required string customer, numeric application_fee, string description, struct metadata, string statement_descriptor, string subscription, numeric tax_percent ) - getInvoice( required string id ) - getInvoiceLineItems( required string id, string customer, string ending_before, numeric limit, string starting_after, string subscription ) - getUpcomingInvoice( required string customer, string subscription ) - updateInvoice( required string id, boolean closed, string description, boolean forgiven, struct metadata, string statement_descriptor, numeric tax_percent ) - payInvoice( required string id ) - listInvoices( string customer, any date, string ending_before, numeric limit, string starting_after ) - -**InvoiceItems** https://stripe.com/docs/api#invoiceitems - - createInvoiceItem( required string customer, required numeric amount, string currency = variables.defaultCurrency, string description, boolean discountable, string invoice, struct metadata, string subscription ) - getInvoiceItem( required string id ) - updateInvoiceItem( required string id, numeric amount, string description, boolean discountable, struct metadata ) - deleteInvoiceItem( required string id ) - listInvoiceItems( any created, string customer, string ending_before, numeric limit, string starting_after ) - -**Disputes** https://stripe.com/docs/api#disputes - - updateChargeDispute( required string id, struct evidence, struct metadata ) - closeChargeDispute( required string id ) - -**Transfers** https://stripe.com/docs/api#transfers - - createTransfer( required numeric amount, string currency = variables.defaultCurrency, required string destination, string description, struct metadata, string source_transaction, string statement_descriptor ) - getTransfer( required string id ) - updateTransfer( required string id, string description, struct metadata ) - listTransfers( any created, any date, string ending_before, numeric limit, string recipient, string starting_after, string status ) - -**Transfer Reversals** https://stripe.com/docs/api#transfer_reversals - - createTransferReversal( required string transfer_id, numeric amount, struct metadata, boolean refund_application_fee ) - getTransferReversal( required string transfer_id, required string id ) - updateTransferReversal( required string transfer_id, required string id, string description, struct metadata ) - listTransferReversals( required string transfer_id, string ending_before, numeric limit, string starting_after ) - -**Application Fees** https://stripe.com/docs/api#application_fees - - getApplicationFee( required string id ) - listApplicationFees( string charge, any created, string ending_before, numeric limit, string starting_after ) - -**Fee Refunds** https://stripe.com/docs/api#fee_refunds - - createApplicationFeeRefund( required string application_fee_id, numeric amount, struct metadata ) - getApplicationFeeRefund( required string application_fee_id, required string id ) - updateApplicationFeeRefund( required string application_fee_id, required string id, struct metadata ) - listApplicationFeeRefunds( required string application_fee_id, string ending_before, numeric limit, string starting_after ) - -**Account** https://stripe.com/docs/api#account - - createAccount( any bank_account, string business_name, string business_url, string country, boolean debit_negative_balances, string default_currency, string email, struct legal_entity, boolean managed, struct metadata, string string, string statement_descriptor, string support_phone, struct tos_acceptance, struct transfer_schedule ) - getAccount( string id ) - updateAccount( required string id, any bank_account, string business_name, string business_url, boolean debit_negative_balances, string default_currency, string email, struct legal_entity, struct metadata, string string, string statement_descriptor, string support_phone, struct tos_acceptance, struct transfer_schedule ) - listAccounts( string ending_before, numeric limit, string starting_after ) - -**Balance** https://stripe.com/docs/api#balance - - getBalance( ) - getBalanceTransaction( required string id ) - listBalanceHistory( any available_on, any created, string currency = variables.defaultCurrency, string ending_before, numeric limit, string source, string starting_after, string transfer, string type ) - -**Events** https://stripe.com/docs/api#events - - getEvent( required string id ) - listEvents( any created, string ending_before, numeric limit, string starting_after, string type ) - -**Tokens** https://stripe.com/docs/api#tokens - - createCardToken( any card, string customer ) - createBankAccountToken( struct bank_account ) - getToken( required string id ) - -**Bitcoin Receivers** https://stripe.com/docs/api#bitcoin_receivers - - createBitcoinReceiver( required numeric amount, string currency = variables.defaultCurrency, string email, string description, struct metadata, boolean refund_mispayments ) - getBitcoinReceiver( required string id ) - listBitcoinReceivers( boolean active, string ending_before, boolean filled, numeric limit, string starting_after, boolean uncaptured_funds ) - -**File Uploads** https://stripe.com/docs/api#file_uploads - - createFileUpload( required string file, required string purpose ) - getFileUpload( required string id ) - listFileUploads( any created, string ending_before, numeric limit, string purpose, string starting_after ) \ No newline at end of file +# stripe-cfml + +**stripe-cfml** is a CFML (Lucee and ColdFusion) library for interacting with the Stripe API. + +## Getting Started + +```cfc +// To charge $20 to a card for which a card token has been created +stripe = new path.to.stripe('stripe_api_key'); + +charge = stripe.charges.create({amount: 2000, currency: 'usd', source: cardToken}); +// OR +charge = stripe.charges.create(amount = 2000, currency = 'usd', source = cardToken); + +// charge is a struct which can be inspected for the result of the create charge api call +writeDump(charge); +``` + +**stripe-cfml** is modeled after the official Stripe SDKs. In particular it copies the class and method names used in the Node SDK. The Node examples given in the official Stripe documentation are simply able to be copied and used (with the notable difference that this library does not make use of callbacks - everything is done synchronously). However, since CFML supports named arguments, you can also use named arguments instead of passing the arguments in a single struct. The following examples are all valid ways of using this library: + +```cfc +stripe.customers.updateSource('customer_id', 'source_id', {metadata = {'a': 1}}); +stripe.customers.updateSource(customer_id = 'customer_id', source_id = 'source_id', params = {metadata: {'a': 1}}); +stripe.customers.updateSource(customer_id = 'customer_id', source_id = 'source_id', metadata = {'a': 1}); +``` + +Note that the `customers` component and the method name, `updateSource`, do not change, only the how the arguments are passed to the method. See the [reference](reference.md) for more information on the method signatures. + +## Headers + +There are a several arguments you can pass to any method that are passed to the Stripe API as headers: `apiKey`, `stripeVersion` (or `apiVersion`), `idempotencyKey`, and `stripeAccount`. + +- `apiKey` and `stripeVersion` will override the default values passed into the `stripe.cfc` init method (see [Configuration](#Configuration) below). +- `idempotencyKey` is used to allow you to make idempotent requests (see https://stripe.com/docs/api#idempotent_requests). +- `stripeAccount` is used when making API requests on behalf of a connected account - see https://stripe.com/docs/connect/authentication. + +When copying the Node SDK method signatures, headers are passed in a single struct: + +```cfc +charge = stripe.charges.create( + {amount: 2000, currency: 'usd', source: cardToken}, + {stripeAccount: 'abc', idempotencyKey: 'def'} +); +``` + +They can also be passed as named arguments: + +```cfc +charge = stripe.charges.create( + amount = 2000, + currency = 'usd', + source = cardToken, + tripeAccount = 'abc', + idempotencyKey = 'def' +); +// OR +charge = stripe.charges.create( + params = {amount: 2000, currency: 'usd', source: cardToken}, + headers = {stripeAccount: 'abc', idempotencyKey: 'def'} +); +``` + +## Configuration + +You can pass some configuration parameters in a struct to the constructor of `stripe.cfc`: + +```cfc +config = { + apiVersion: '', + defaultCurrency: '', + convertTimestamps: true, + convertToCents: false +} +stripe = new stripe('stripe_api_key', config); +``` + +- `apiVersion` specifies the version of the Stripe API to use - see [versioning](https://stripe.com/docs/api#versioning) in the documentation. +- `defaultCurrency` specifies the currency to use when making requests (e.g. `usd`) - when it is specified in the config you do not need to specify it when making requests. +- `convertTimestamps` (default: `true`) - The Stripe API expects all datetimes to be given as Unix timestamps; when this setting is true, `stripe-cfml` converts all CFML date objects passed into methods to UNIX timestamps and converts timestamps in the API responses back to CFML date objects. +- `convertToCents` (default: `false`) - when this is set to `true` all currency amounts passed in are multiplied by 100 and all amounts in the responses are divided by 100. (This enables one to work in dollar amounts instead of cents, if so desired.) + +### Configuration via environment variables and system properties + +All of these configuration keys, including the Stripe secret key, can be specified in environment variables or Java system properties instead of being passed in at initialization. When using environment variables your config keys should be prefixed with `STRIPE_` and underscores are used to separate words: `STRIPE_API_KEY`, `STRIPE_API_VERSION`, `STRIPE_DEFAULT_CURRENCY`, `STRIPE_CONVERT_TIMESTAMPS`, and `STRIPE_CONVERT_TO_CENTS`. When using system properties your config keys should be prefixed with `stripe.` and all lowercase: `stripe.apikey`, `stripe.apiversion`, `stripe.defaultcurrency`, `stripe.converttimestamps`, and `stripe.converttocents`. + +## Responses + +The Stripe API returns a JSON object in response to all HTTP requests (see https://stripe.com/docs/api). **stripe-cfml** deserializes this object into a CFML struct and makes it available in a response struct under the `content` key. + +Responses to API calls are all returned as structs in the following format: + +```cfc +{ + requestId: '' // request identifier - see https://stripe.com/docs/api#request_ids + content: {} // struct containing the body of the response + duration: 300 // time the HTTP request took in milliseconds + headers: {} // struct containing headers returned by the Stripe API + status: 200 // status code returned by the Stripe API +} +``` + +## Webhooks + +**stripe-cfml** can verify signed webhooks received by your server in a similar fashion to the official Stripe SDKs: + +```cfc +try { + event = stripe.webhooks.constructEvent(webhookJSONPayload, stripeSignatureHeader, endpointSecret); +} catch (any e) { + // the webhook was not signed properly +} +// otherwise do something with the event + +``` + +See and for more information on setting up signed webhooks. diff --git a/box.json b/box.json new file mode 100644 index 0000000..3d745c8 --- /dev/null +++ b/box.json @@ -0,0 +1,12 @@ +{ + "devDependencies": { + "testbox": "^2.6.0+156" + }, + "installPaths": { + "testbox": "testbox" + }, + "testbox": { + "runner": "http://127.0.0.1:45000/tests/runner.cfm", + "tests_root": "" + } +} diff --git a/generatemethodindex.cfm b/generatemethodindex.cfm deleted file mode 100644 index 6df5bea..0000000 --- a/generatemethodindex.cfm +++ /dev/null @@ -1,70 +0,0 @@ - -metadata = getComponentMetaData( "stripe" ); -functionArrayLength = arrayLen( metadata.functions ); -functionStruct = { }; -functionNameArray = [ ]; -apiRoutes = [ 'Charges','Refunds','Customers','Cards','Subscriptions','Plans','Coupons','Discounts','Invoices','InvoiceItems','Disputes','Transfers','Transfer Reversals','Application Fees','Fee Refunds','Account','Balance','Events','Tokens','Bitcoin Receivers','File Uploads' ]; -methodSignatures = [ 'Charge','ChargeRefund','Customer','CustomerSource','CustomerSubscription','Plan','Coupon',[ 'CustomerDiscount','CustomerSubscriptionDiscount' ],[ 'Invoice','InvoiceLineItem','UpcomingInvoice' ],'InvoiceItem','ChargeDispute','Transfer','TransferReversal','ApplicationFee','ApplicationFeeRefund','Account',[ 'Balance','BalanceTransaction','BalanceHistory' ],'Event',[ 'Token','CardToken','BankAccountToken' ],'BitcoinReceiver','FileUpload' ]; - -for ( i = 1; i <= functionArrayLength; i++ ) { - if ( metadata.functions[ i ].access == "public" and metadata.functions[ i ].name != "init" ) { - arrayAppend( functionNameArray, metadata.functions[ i ].name ); - parameters = [ ]; - for ( parameter in metadata.functions[ i ].parameters ) { - arrayAppend( parameters, ( parameter.required ? 'required ' : '' ) & parameter.type & ' ' & parameter.name & ( parameter.name eq "currency" ? ' = variables.defaultCurrency' : '' ) ); - } - functionStruct[ metadata.functions[ i ].name ] = '#metadata.functions[ i ].name#( #arrayToList( parameters, ", " )# )'; - } -} - -html = ''; -markdown = ''; - -for ( i = 1; i <= arrayLen( apiRoutes ); i++ ) { - - apiRoute = apiRoutes[ i ]; - aLink = 'https://stripe.com/docs/api###lcase( replace( apiRoute, ' ', '_', 'all' ) )#'; - - html &= '

' & apiRoute & ' #aLink#

' & chr( 10 ) & '
' & chr( 10 );
-	markdown &= chr( 10 ) & '**' & apiRoute & '** ' & aLink & chr( 10 ) & chr( 10 );
-
-	for ( functionName in functionNameArray ) {
-		if ( !isArray( methodSignatures[ i ] ) ) methodSignatures[ i ] = [ methodSignatures[ i ] ];
-		for ( signature in methodSignatures[ i ] ) {
-			if ( rereplace( functionName, '([a-z]+)',"" ) == signature || rereplace( functionName, '([a-z]+)',"" ) == signature & 's' ) {
-				html &= functionStruct[ functionName ] & chr( 10 );
-				markdown &= chr( 9 ) & chr( 9 ) & functionStruct[ functionName ] & chr( 10 );
-				structDelete( functionStruct, functionName );
-				break;
-			}
-		}
-	}
-
-	html &= '
' & chr( 10 ); - -} - -html &= '

Miscellaneous

';
-markdown &= chr( 10 ) & '**Miscellaneous**' & chr( 10 ) & chr( 10 );
-
-
-for ( functionName in functionNameArray ) {
-	if ( structKeyExists( functionStruct, functionName ) ) {
-		html &= functionStruct[ functionName ] & chr( 10 );
-		markdown &= chr( 9 ) & chr( 9 ) & functionStruct[ functionName ] & chr( 10 );
-	}
-}
-
-html &= '
'; - -
- - -
-
-
-
-
- -#html# -
\ No newline at end of file diff --git a/index.cfm b/index.cfm deleted file mode 100644 index 7833300..0000000 --- a/index.cfm +++ /dev/null @@ -1,228 +0,0 @@ - - - - stripe.cfc - - - - - - - - -
- -

stripe.cfc is a(nother) Railo/ColdFusion wrapper for the stripe.com API.

- -

It was tested against and supports all of the routes of the 2015-03-24 version of the stripe.com API. It should run on Railo 3.3+ and ColdFusion 9.0.1+.

- -

Getting Started

- -
-// To charge $20 to a card for which a card token has been created
-stripe = new path.to.stripe( 'STRIPE_SECRET_KEY' );
-charge = stripe.createCharge( amount = 2000, source = card_token );
-// charge is a struct which can be inspected for the result of the create charge api call
-writeDump( charge );
-
- -

Changelog

- -

Updated to 2015-03-24 version of the stripe.com API

- -
    -
  • IMPORTANT card and default_card have been replaced with source and default_source. This means that in the createCharge method, for instance, the card argument has been renamed to source. Correspondingly, the CustomerCard methods have been replaced with CustomerSource methods.
  • -
  • In addition to the apiKey argument mentioned below, there are now two other arguments that can be passed to any method: stripeAccount and idempotencyKey. When either argument is included stripe.cfc will add a Stripe-Account or Idempotency-Key header to the api request respectively.
  • -
  • The recipients api endpoint has been deprecated.
  • -
- -

Updated to 2014-03-28 version of the stripe.com API

- -
    -
  • Although not listed in any method's arguments, it is now possible to pass in an apiKey argument containing a stripe secret key or an access token (for use with Stripe Connect) to any method, and stripe.cfc will use that api key for the api request instead of the default api key passed in when stripe.cfc was initialized.
  • -
  • Method arguments have been reordered, with ids first, followed by required arguments in alphabetical order, and then optional arguments in alphabetical order. It is recommended to pass arguments into methods by name, to avoid running into issues caused by the reordering, removal, or addition of arguments.
  • -
  • Pagination has undergone a significant change. The count and offset arguments are gone. They have been replaced by limit, starting_after, and ending_before. Both starting_after and ending_before take a stripe object ID as a value. See https://stripe.com/docs/api#pagination. While not listed in any list<Object>s() methods, stripe.cfc looks for an include array argument that can be used to get the total count of records when using a list method by passing in include = [ "total_count" ] to the method.
  • -
  • It should also be mentioned that there is a similar expand array argument that can be used to expand certain properties of the stripe API response so that it includes more detail. See https://stripe.com/docs/api#expand.
  • -
- -

More Details about stripe.cfc

- -

By default, stripe.cfc converts all UTC timestamps to and from CFML date variables, but this can be disabled by passing in convertUTCTimestamps = false when stripe.cfc is initialized.

- -

To use dollars instead of cents, pass convertToCents = true into the init() method when stripe.cfc is created. Stripe.cfc will then multiply all input currency amounts by 100, and divide all return currency amounts by 100. For example:

- -
-stripe = new path.to.stripe( stripeSecretKey = 'STRIPE_SECRET_KEY', convertToCents = true );
-charge = stripe.createCharge( amount = 19.99, card = cardToken );
-// stripe.cfc will convert the 19.99 to 1999 before calling the API
-
- -

The default currency can also be set at init: defaultCurrency = "usd", as well as overridden on a method by method basis.

- -

What is Returned

- -

The stripe.com API returns a JSON object in response to all api requests (see https://stripe.com/docs/api). Stripe.cfc deserializes this object into a CFML struct. It also adds three key-value pairs to this struct: 'api_request_time', 'status_code', and 'status_text'. 'api_request_time' is the total time in milliseconds for the HTTP request. 'status_code' and 'status_text' refer to the status code received by the HTTP request. If '200 OK' is received, then 'status_code' will be '200' and 'status_text' will be 'OK'.

- -

Stripe.cfc by default converts all UTC timestamps in the returned JSON object to CFML dates.

- -

Testing

- -

There are some unit tests that can be run. They depend on mxunit and require a mapping of 'mxunit' pointing to the directory of the mxunit framework. Running these are a good way to see some example results.

-
Warning: these tests take significant time to complete, as they can each make multiple calls to the stripe.com API in a single test and so can take several seconds to complete. If you run them, make sure you use a valid testing (not live) key!
- - -

Available Methods

-

Charges https://stripe.com/docs/api#charges

-
-createCharge( required numeric amount, string currency = variables.defaultCurrency, numeric application_fee, boolean capture, string customer, string description, string destination, struct metadata, string receipt_email, struct shipping, any source, string statement_descriptor )
-getCharge( required string id )
-updateCharge( required string id, string description, struct metadata )
-captureCharge( required string id, numeric amount, numeric application_fee )
-listCharges( any created, string customer, string ending_before, numeric limit, string starting_after )
-
-

Refunds https://stripe.com/docs/api#refunds

-
-createChargeRefund( required string id, numeric amount, struct metadata, string reason, boolean refund_application_fee )
-getChargeRefund( required string charge_id, required string id )
-updateChargeRefund( required string charge_id, required string id, struct metadata )
-listChargeRefunds( required string charge_id, string ending_before, numeric limit, string starting_after )
-
-

Customers https://stripe.com/docs/api#customers

-
-createCustomer( numeric account_balance, string coupon, string description, string email, struct metadata, string plan, numeric quantity, any source, any trial_end )
-getCustomer( required string id )
-updateCustomer( required string id, numeric account_balance, string coupon, string default_source, string description, string email, struct metadata, any source )
-deleteCustomer( required string id )
-listCustomers( any created, string ending_before, numeric limit, string starting_after )
-
-

Cards https://stripe.com/docs/api#cards

-
-createCustomerSource( required string customer_id, any source )
-getCustomerSource( required string customer_id, required string id )
-updateCustomerSource( required string customer_id, required string id, string address_city, string address_country, string address_line1, string address_line2, string address_state, string address_zip, numeric exp_month, numeric exp_year, struct metadata, string name )
-deleteCustomerSource( required string customer_id, required string id )
-listCustomerSources( required string customer_id, string ending_before, numeric limit, string starting_after )
-
-

Subscriptions https://stripe.com/docs/api#subscriptions

-
-createCustomerSubscription( required string customer_id, required string plan, numeric application_fee_percent, string coupon, struct metadata, boolean prorate, numeric quantity, any source, numeric tax_percent, any trial_end )
-getCustomerSubscription( required string customer_id, required string id )
-updateCustomerSubscription( required string customer_id, required string id, numeric application_fee_percent, string coupon, struct metadata, string plan, boolean prorate, numeric quantity, any source, numeric tax_percent, any trial_end )
-cancelCustomerSubscription( required string customer_id, required string id, boolean at_period_end )
-listCustomerSubscriptions( required string customer_id, string ending_before, numeric limit, string starting_after )
-
-

Plans https://stripe.com/docs/api#plans

-
-createPlan( required string id, required numeric amount, string currency = variables.defaultCurrency, required string interval, required string name, numeric interval_count, struct metadata, string statement_descriptor, numeric trial_period_days )
-getPlan( required string id )
-updatePlan( required string id, struct metadata, string name, string statement_descriptor )
-deletePlan( required string id )
-listPlans( any created, string ending_before, numeric limit, string starting_after )
-
-

Coupons https://stripe.com/docs/api#coupons

-
-createCoupon( required string duration, numeric amount_off, string currency = variables.defaultCurrency, numeric duration_in_months, string id, numeric max_redemptions, struct metadata, numeric percent_off, any redeem_by )
-getCoupon( required string id )
-updateCoupon( required string id, struct metadata )
-deleteCoupon( required string id )
-listCoupons( any created, string ending_before, numeric limit, string starting_after )
-
-

Discounts https://stripe.com/docs/api#discounts

-
-deleteCustomerDiscount( required string id )
-deleteCustomerSubscriptionDiscount( required string customer_id, required string id )
-
-

Invoices https://stripe.com/docs/api#invoices

-
-createInvoice( required string customer, numeric application_fee, string description, struct metadata, string statement_descriptor, string subscription, numeric tax_percent )
-getInvoice( required string id )
-getInvoiceLineItems( required string id, string customer, string ending_before, numeric limit, string starting_after, string subscription )
-getUpcomingInvoice( required string customer, string subscription )
-updateInvoice( required string id, boolean closed, string description, boolean forgiven, struct metadata, string statement_descriptor, numeric tax_percent )
-payInvoice( required string id )
-listInvoices( string customer, any date, string ending_before, numeric limit, string starting_after )
-
-

InvoiceItems https://stripe.com/docs/api#invoiceitems

-
-createInvoiceItem( required string customer, required numeric amount, string currency = variables.defaultCurrency, string description, boolean discountable, string invoice, struct metadata, string subscription )
-getInvoiceItem( required string id )
-updateInvoiceItem( required string id, numeric amount, string description, boolean discountable, struct metadata )
-deleteInvoiceItem( required string id )
-listInvoiceItems( any created, string customer, string ending_before, numeric limit, string starting_after )
-
-

Disputes https://stripe.com/docs/api#disputes

-
-updateChargeDispute( required string id, struct evidence, struct metadata )
-closeChargeDispute( required string id )
-
-

Transfers https://stripe.com/docs/api#transfers

-
-createTransfer( required numeric amount, string currency = variables.defaultCurrency, required string destination, string description, struct metadata, string source_transaction, string statement_descriptor )
-getTransfer( required string id )
-updateTransfer( required string id, string description, struct metadata )
-listTransfers( any created, any date, string ending_before, numeric limit, string recipient, string starting_after, string status )
-
-

Transfer Reversals https://stripe.com/docs/api#transfer_reversals

-
-createTransferReversal( required string transfer_id, numeric amount, struct metadata, boolean refund_application_fee )
-getTransferReversal( required string transfer_id, required string id )
-updateTransferReversal( required string transfer_id, required string id, string description, struct metadata )
-listTransferReversals( required string transfer_id, string ending_before, numeric limit, string starting_after )
-
-

Application Fees https://stripe.com/docs/api#application_fees

-
-getApplicationFee( required string id )
-listApplicationFees( string charge, any created, string ending_before, numeric limit, string starting_after )
-
-

Fee Refunds https://stripe.com/docs/api#fee_refunds

-
-createApplicationFeeRefund( required string application_fee_id, numeric amount, struct metadata )
-getApplicationFeeRefund( required string application_fee_id, required string id )
-updateApplicationFeeRefund( required string application_fee_id, required string id, struct metadata )
-listApplicationFeeRefunds( required string application_fee_id, string ending_before, numeric limit, string starting_after )
-
-

Account https://stripe.com/docs/api#account

-
-createAccount( any bank_account, string business_name, string business_url, string country, boolean debit_negative_balances, string default_currency, string email, struct legal_entity, boolean managed, struct metadata, string string, string statement_descriptor, string support_phone, struct tos_acceptance, struct transfer_schedule )
-getAccount( string id )
-updateAccount( required string id, any bank_account, string business_name, string business_url, boolean debit_negative_balances, string default_currency, string email, struct legal_entity, struct metadata, string string, string statement_descriptor, string support_phone, struct tos_acceptance, struct transfer_schedule )
-listAccounts( string ending_before, numeric limit, string starting_after )
-
-

Balance https://stripe.com/docs/api#balance

-
-getBalance(  )
-getBalanceTransaction( required string id )
-listBalanceHistory( any available_on, any created, string currency = variables.defaultCurrency, string ending_before, numeric limit, string source, string starting_after, string transfer, string type )
-
-

Events https://stripe.com/docs/api#events

-
-getEvent( required string id )
-listEvents( any created, string ending_before, numeric limit, string starting_after, string type )
-
-

Tokens https://stripe.com/docs/api#tokens

-
-createCardToken( any card, string customer )
-createBankAccountToken( struct bank_account )
-getToken( required string id )
-
-

Bitcoin Receivers https://stripe.com/docs/api#bitcoin_receivers

-
-createBitcoinReceiver( required numeric amount, string currency = variables.defaultCurrency, string email, string description, struct metadata, boolean refund_mispayments )
-getBitcoinReceiver( required string id )
-listBitcoinReceivers( boolean active, string ending_before, boolean filled, numeric limit, string starting_after, boolean uncaptured_funds )
-
-

File Uploads https://stripe.com/docs/api#file_uploads

-
-createFileUpload( required string file, required string purpose )
-getFileUpload( required string id )
-listFileUploads( any created, string ending_before, numeric limit, string purpose, string starting_after )
-
- -
- - - \ No newline at end of file diff --git a/lib/config.cfc b/lib/config.cfc new file mode 100644 index 0000000..583871e --- /dev/null +++ b/lib/config.cfc @@ -0,0 +1,51 @@ +component { + + variables.settings = { + api_key: '', + api_version: '', + convert_timestamps: true, + convert_to_cents: false, + default_currency: '', + endpoint: 'api.stripe.com/v1', + stripe_version: '' + }; + + public any function init( string apiKey = '', struct config = { } ) { + loadEnvSettings(); + mergeConfig( config ); + if ( len( apiKey ) ) { + settings.api_key = apiKey; + } + return this; + } + + public any function get( required string key ) { + if ( settings.keyExists( key ) ) return settings[ key ]; + return ''; + } + + private void function loadEnvSettings() { + var system = createObject( 'java', 'java.lang.System' ); + for ( var key in settings ) { + var envKey = key.left( 6 ) != 'stripe' ? 'STRIPE_#key.ucase()#' : key.ucase(); + var envSetting = system.getenv( envKey ); + if ( !isNull( envSetting ) ) settings[ key ] = envSetting; + var propKey = envKey.lcase() + .replace( 'stripe_', 'stripe.' ) + .replace( '_', '', 'all' ); + var propSetting = system.getProperty( propKey ); + if ( !isNull( propSetting ) ) settings[ key ] = propSetting; + } + } + + private void function mergeConfig( required struct config ) { + for ( var key in settings ) { + if ( config.keyExists( key ) ) { + settings[ key ] = config[ key ]; + } else if ( config.keyExists( key.replace( '_', '', 'all' ) ) ) { + settings[ key ] = config[ key.replace( '_', '', 'all' ) ]; + } + } + } + +} diff --git a/lib/httpService.cfc b/lib/httpService.cfc new file mode 100644 index 0000000..ab3987b --- /dev/null +++ b/lib/httpService.cfc @@ -0,0 +1,114 @@ +component { + + public any function init() { + variables.serverVersion = server.keyExists( 'lucee' ) ? 'Lucee' : 'ColdFusion'; + return this; + } + + public any function makeRequest( + required string httpMethod, + required string path, + struct headers = { }, + array params = [ ], + boolean multipart = false, + numeric timeout = 50 + ) { + var httpRequest = { attrColl: { method: httpMethod, url: path, timeout: timeout } }; + + if ( serverVersion == 'lucee' ) { + httpRequest.attrColl[ 'encodeurl' ] = false; + } + + if ( arrayFindNoCase( [ 'POST', 'PUT' ], httpMethod ) ) { + if ( multipart ) { + var multipartEntity = multipartFormData( params ); + headers[ 'Content-Type' ] = multipartEntity.contentType; + httpRequest.body = multipartEntity.body; + } else { + headers[ 'Content-Type' ] = 'application/x-www-form-urlencoded'; + httpRequest.body = parseQueryParams( params ); + } + } else if ( !params.isEmpty() ) { + httpRequest.attrColl.url &= '?' & parseQueryParams( params ); + } + + httpRequest.headers = parseHeaders( headers ); + + // writeDump( arguments ); + // writeDump( httpRequest ); + + return exec( httpRequest ); + } + + private struct function exec( required struct httpRequest ) { + var result = ''; + httpRequest.attrColl.result = 'result'; + cfhttp( attributeCollection=httpRequest.attrColl ) { + for ( var header in httpRequest.headers ) { + cfhttpparam( type='header', name=header.name, value=header.value ); + } + if ( arrayFindNoCase( [ 'POST', 'PUT' ], httpRequest.attrColl.method ) ) { + cfhttpparam( type='body', value=httpRequest.body ); + } + } + return result; + } + + private array function parseHeaders( required struct headers ) { + var sortedKeyArray = headers.keyArray(); + sortedKeyArray.sort( 'textnocase' ); + var processedHeaders = sortedKeyArray.map( function( key ) { return { name: key, value: trim( headers[ key ] ) }; } ); + return processedHeaders; + } + + private string function parseQueryParams( required array params ) { + params.sort( function( a, b ) { + return a.name > b.name ? 1 : a.name < b.name ? -1 : 0; + } ); + var queryStringElements = arrayMap( params, function( param ) { + return encodeUrl( param.name ) & '=' & encodeUrl( param.value ); + } ); + return queryStringElements.toList( '&' ); + } + + private struct function multipartFormData( required array params ) { + var entityBuilder = createObject( 'java', 'org.apache.http.entity.mime.MultipartEntityBuilder' ).create(); + var binaryFields = [ ]; + for ( var param in params ) { + if ( isBinary( param.value ) ) { + var prefix = param.name.replace( '[data]', '' ); + var contentTypeKey = prefix & '[type]'; + var filenameKey = prefix & '[name]'; + + var filenameIndex = params.find( function( p ) { + return p.name == filenameKey; + } ); + var filename = params[ filenameIndex ].value; + + var contentTypeIndex = params.find( function( p ) { + return p.name == contentTypeKey; + } ); + var contentTypeValue = contentTypeIndex ? params[ contentTypeIndex ].value : 'application/octet-stream'; + var contentType = createObject( 'java', 'org.apache.http.entity.ContentType' ).create( contentTypeValue ); + + entityBuilder.addBinaryBody( javacast( 'string', prefix ), param.value, contentType, javacast( 'string', filename ) ); + binaryFields.append( [ param.name, contentTypeKey, filenameKey ], true ); + } + } + for ( var param in params ) { + if ( !binaryFields.find( param.name ) ) { + entityBuilder.addTextBody( javacast( 'string', param.name ), javacast( 'string', param.value ) ); + } + } + var entity = entityBuilder.build(); + var outputStream = createObject( 'java', 'java.io.ByteArrayOutputStream' ).init( entity.getContentLength() ); + entity.writeTo( outputStream ); + return { contentType: entity.getContentType().getValue(), body: outputStream.toByteArray() }; + } + + private string function encodeUrl( required string str ) { + return replacelist( urlEncodedFormat( arguments.str, "utf-8" ), "%2D,%2E,%5F,%7E", "-,.,_,~" ); + } + +} + diff --git a/lib/parsers/arguments.cfc b/lib/parsers/arguments.cfc new file mode 100644 index 0000000..23de31d --- /dev/null +++ b/lib/parsers/arguments.cfc @@ -0,0 +1,167 @@ +component { + + variables.argumentTypes = { + datefilter: { + _complex: { + 'gt': 'timestamp', + 'gte': 'timestamp', + 'lt': 'timestamp', + 'lte': 'timestamp' + }, + _simple: 'timestamp' + } + }; + + public any function init( required any config ) { + variables.config = config; + variables.parserUtils = new parserUtils(); + return this; + } + + public array function parse( + required struct argCollection, + required struct methodArgumentMetdata, + required array ignoredArgs + ) { + var filteredArgs = { }; + var argKeys = structKeyArray( argCollection ); + for ( var argKey in argKeys ) { + // don't include ignored args (path args and headers) + if ( ignoredArgs.findNoCase( argKey ) ) { + continue; + } + // don't include null arguments + if ( isNull( argCollection[ argKey ] ) ) { + continue; + } + filteredArgs[ argKey ] = argCollection[ argKey ]; + } + var parsed = parseStruct( filteredArgs, methodArgumentMetdata ); + return flatten( parsed ); + } + + private struct function parseStruct( required any args, required struct methodArgumentMetdata ) { + var parsed = { }; + + for ( var key in structKeyArray( args ) ) { + if ( isNull( args[ key ] ) ) continue; + + var val = args[ key ]; + var argType = parseType( key, methodArgumentMetdata ); + + if ( isSimpleValue( val ) ) { + parsed[ key ] = parseSimpleValue( val, argType._simple ); + } else if ( isBinary( val ) ) { + parsed[ key ] = val; + } else if ( isArray( val ) ) { + parsed[ key ] = [ ]; + for ( var item in val ) { + if ( isSimpleValue( item ) ) { + parsed[ key ].append( parseSimpleValue( item, argType._simple ) ); + } else { + parsed[ key ].append( parseStruct( item, argType._complex ) ); + } + } + } else if ( isStruct( val ) ) { + parsed[ key ] = parseStruct( val, argType._complex ); + } + } + + for ( var key in methodArgumentMetdata ) { + if ( + isSimpleValue( methodArgumentMetdata[ key ] ) && + methodArgumentMetdata[ key ] == 'iso_currency_code' && + !parsed.keyExists( key ) + ) { + parsed[ key ] = config.get( 'default_currency' ); + } + } + + return parsed; + } + + private function parseType( key, methodArgumentMetdata ) { + var base_type = { _simple: 'string', _complex: { } }; + if ( methodArgumentMetdata.keyExists( key ) ) { + var type = methodArgumentMetdata[ key ]; + if ( isStruct( type ) ) { + if ( type.keyExists( '_simple' ) || type.keyExists( '_complex' ) ) { + base_type.append( type ); + } else { + base_type._complex = type; + } + } else if ( argumentTypes.keyExists( type ) ) { + base_type = argumentTypes[ type ]; + } else { + base_type._simple = type; + } + } + return base_type; + } + + private function parseSimpleValue( value, type ) { + switch ( type ) { + case 'currency': + var amount = value; + if (config.get( 'convert_to_cents' )) { + amount = round( value * 100 ); + } + return parseInteger( amount ); + case 'timestamp': + return parseUTCTimestampField( value ); + case 'integer': + return parseInteger( value ); + } + return trim( value ); + } + + private array function flatten( src, base_key ) { + var flattened = [ ]; + for ( var k in src ) { + var outKey = !isNull( base_key ) ? '#base_key#[#k#]' : k; + if ( isNull( base_key ) || base_key != 'metadata' ) { + outKey = lcase( outKey ); + } + + if ( isSimpleValue( src[ k ] ) ) { + flattened.append( { name: outKey, value: src[ k ] } ); + } else if ( isBinary( src[ k ] ) ) { + flattened.append( { name: outKey, value: src[ k ] } ); + } else if ( isStruct( src[ k ] ) ) { + if ( structIsEmpty( src[ k ] ) ) { + flattened.append( { name: outKey, value: '' } ); + } else { + var nested = flatten( src[ k ], outKey ); + flattened.append( nested, true ); + } + } else if ( isArray( src[ k ] ) ) { + var arrIndex = 0; + for ( var item in src[ k ] ) { + var arr_key = outKey & '[#arrIndex#]'; + if ( isStruct( item ) ) { + flattened.append( flatten( item, arr_key ), true ); + } else { + flattened.append( { name: arr_key, value: item } ); + } + arrIndex++; + } + } + } + return flattened; + } + + private any function parseUTCTimestampField( required any utcField ) { + if ( parserUtils.isInteger( utcField ) ) return utcField; + if ( isDate( utcField ) ) return parserUtils.getUTCTimestamp( utcField ); + if ( utcField == 'now' ) return 'now'; + throw( "utc timestamp field is in an invalid format" ); + } + + private function parseInteger( v ) { + if ( !parserUtils.isInteger( v ) ) { + throw( '`#v#` must be an integer.' ); + } + return int(v); + } + +} diff --git a/lib/parsers/headers.cfc b/lib/parsers/headers.cfc new file mode 100644 index 0000000..591a4ef --- /dev/null +++ b/lib/parsers/headers.cfc @@ -0,0 +1,45 @@ +component { + + variables.headerKeys = { + 'api_key': 'Authorization', + 'api_version': 'Stripe-Version', + 'stripe_version': 'Stripe-Version', + 'idempotency_key': 'Idempotency-Key', + 'stripe_account': 'Stripe-Account' + }; + + public any function init( required any config ) { + variables.config = config; + return this; + } + + public struct function parse( required any headersSource, required struct methodMetadata ) { + var headerData = { headers: { }, headerArgNames: [ ] }; + + for ( var key in headerKeys ) { + var headerName = headerKeys[ key ]; + var argNames = [ key, key.replace( '_', '' ) ]; + headerData.headerArgNames.append( argNames, true ); + + var configSetting = config.get( key ); + if ( len( configSetting ) ) { + headerData.headers[ headerName ] = configSetting; + } + + for ( var argName in argNames ) { + if ( structKeyExists( headersSource, argName ) ) { + headerData.headers[ headerName ] = headersSource[ argName ]; + } + } + } + + // auth header is required and needs to be prefixed with `Bearer` + if ( !headerData.headers.keyExists( 'Authorization' ) ) { + throw( 'Stripe requires an API key but none has been set.' ); + } + headerData.headers[ 'Authorization' ] = 'Bearer ' & headerData.headers[ 'Authorization' ]; + + return headerData; + } + +} diff --git a/lib/parsers/parserUtils.cfc b/lib/parsers/parserUtils.cfc new file mode 100644 index 0000000..e46137d --- /dev/null +++ b/lib/parsers/parserUtils.cfc @@ -0,0 +1,22 @@ +component { + + public any function init() { + variables.utcBaseDate = createObject( 'java', 'java.util.Date' ).init( javacast( 'int', 0 ) ); + return this; + } + + public numeric function getUTCTimestamp( required date dateToConvert ) { + var asDate = parseDateTime( dateToConvert ); + return dateDiff( 's', variables.utcBaseDate, asDate ); + } + + public date function parseUTCTimestamp( required numeric utcTimestamp ) { + var parsed_date = dateAdd( 's', arguments.utcTimestamp, variables.utcBaseDate ); + return parsed_date; + } + + public boolean function isInteger( required any varToValidate ) { + return ( isNumeric( arguments.varToValidate ) && isValid( 'integer', arguments.varToValidate ) ); + } + +} diff --git a/lib/parsers/response.cfc b/lib/parsers/response.cfc new file mode 100644 index 0000000..6a33b46 --- /dev/null +++ b/lib/parsers/response.cfc @@ -0,0 +1,70 @@ +component { + + public any function init( required any config, required struct objectMetadata ) { + variables.config = arguments.config; + variables.objectMetadata = arguments.objectMetadata; + variables.parserUtils = new parserUtils(); + return this; + } + + public void function parse( required struct response ) { + if ( response.keyExists( 'object' ) && isSimpleValue( response.object ) ) { + var metadata = objectMetadata.keyExists( response.object ) ? objectMetadata[ response.object ] : { }; + + // almost all objects have a `created` timestamp + // adding a check here so any object metadata that only + // needs to register `created` can be implicit + if ( !metadata.keyExists( 'created' ) ) { + metadata[ 'created' ] = 'datetime'; + } + + parseResTypes( response, metadata ); + } + + for ( var key in response ) { + if ( !response.keyExists( key ) || isNull( response[ key ] ) ) continue; + if ( isStruct( response[ key ] ) ) { + parse( response[ key ] ); + } else if ( isArray( response[ key ] ) ) { + for ( var item in response[ key ] ) { + if ( isStruct( item ) ) { + parse( item ); + } + } + } + } + } + + private void function parseResTypes( res, metadata ) { + for ( var key in metadata ) { + if ( !structKeyExists( res, key ) || isNull( res[ key ] ) ) { + continue; + } + + if ( isStruct( metadata[ key ] ) ) { + if ( isStruct( res[ key ] ) ) { + parseResTypes( res[ key ], metadata[ key ] ); + } else if ( isArray( res[ key ] ) ) { + for ( var item in res[ key ] ) { + if ( isStruct( item ) ) { + parseResTypes( item, metadata[ key ] ); + } + } + } + } else { + switch ( metadata[ key ] ) { + case 'datetime': + if ( config.get( 'convert_timestamps' ) ) { + res[ key ] = parserUtils.parseUTCTimestamp( res[ key ] ); + } + break; + case 'currency': + if ( config.get( 'convert_to_cents' ) ) { + res[ key ] = res[ key ] / 100; + } + break; + } + } + } + } +} diff --git a/lib/resources/abstract/apiResource.cfc b/lib/resources/abstract/apiResource.cfc new file mode 100644 index 0000000..8991059 --- /dev/null +++ b/lib/resources/abstract/apiResource.cfc @@ -0,0 +1,55 @@ +component { + + variables.metadata = { }; + + function init( required any stripe, required any config ) { + variables.stripe = arguments.stripe; + variables.config = arguments.config; + variables.resourceName = getMetadata( this ).name.listLast( '.' ); + variables.metadata = loadMetadata( variables.metadata ); + return this; + } + + public struct function onMissingMethod( missingMethodName, missingMethodArguments ) { + if ( !metadata.methods.keyExists( missingMethodName ) ) { + var message = '`stripe.#resourceName#.#missingMethodName#()` is not a valid method for `stripe.#resourceName#`. Available methods are #metadata.methodNameList#.'; + throw( message ); + } + return stripe.call( resourceName, missingMethodName, missingMethodArguments, metadata.methods[ missingMethodName ] ); + } + + private struct function loadMetadata( metadata ) { + var loaded = duplicate( metadata ); + var baseMethodMeta = getBaseMethodMetadata(); + for ( var methodName in loaded.methods ) { + loaded.methods[ methodName ].append( baseMethodMeta, false ); + loaded.methods[ methodName ][ 'pathArgs' ] = parsePath( loaded.methods[ methodName ].path ); + if ( !loaded.methods[ methodName ].keyExists( 'positionalArgs' ) ) { + loaded.methods[ methodName ][ 'positionalArgs' ] = loaded.methods[ methodName ].pathArgs; + } + } + + loaded.methodNameList = arrayMap( loaded.methods.keyArray(), function( mn ) { + return 'stripe.#resourceName#.#mn#()'; + } ); + var length = arrayLen( loaded.methodNameList ); + loaded.methodNameList[ length ] = 'and ' & loaded.methodNameList[ length ]; + loaded.methodNameList = loaded.methodNameList.toList( ', ' ); + + return loaded; + } + + private struct function getBaseMethodMetadata() { + return { endpoint: config.get( 'endpoint' ), httpMethod: 'get', multipart: false, arguments: { } }; + } + + private array function parsePath( required string path ) { + return arrayMap( + reMatch( '\{([a-z_]+)\}', path), + function( s ) { + return mid( s, 2, len( s ) - 2 ); + } + ); + } + +} diff --git a/lib/resources/accounts.cfc b/lib/resources/accounts.cfc new file mode 100644 index 0000000..53f7e5a --- /dev/null +++ b/lib/resources/accounts.cfc @@ -0,0 +1,80 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + tos_acceptance: { + date: 'timestamp' + } + }, + httpMethod: 'post', + path: '/accounts' + }, + 'createExternalAccount': { + httpMethod: 'post', + path: '/accounts/{account_id}/external_accounts' + }, + 'createLoginLink': { + httpMethod: 'post', + path: '/accounts/{account_id}/login_links' + }, + 'delete': { + httpMethod: 'delete', + path: '/accounts/{account_id}' + }, + 'deleteExternalAccount': { + httpMethod: 'delete', + path: '/accounts/{account_id}/external_accounts/{source_id}' + }, + 'list': { + path: '/accounts' + }, + 'listExternalAccounts': { + path: '/accounts/{account_id}/external_accounts' + }, + 'reject': { + httpMethod: 'post', + path: '/accounts/{account_id}/reject' + }, + 'retrieve': { + path: '/accounts/{account_id}' + }, + 'retrieveExternalAccount': { + path: '/accounts/{account_id}/external_accounts/{source_id}' + }, + 'update': { + arguments: { + tos_acceptance: { + date: 'timestamp' + } + }, + httpMethod: 'post', + path: '/accounts/{account_id}' + }, + 'updateExternalAccount': { + httpMethod: 'post', + path: '/accounts/{account_id}/external_accounts/{source_id}' + } + } + }; + + public struct function retrieve() { + if ( + arrayLen( arguments ) == 0 || + ( + !structKeyExists( arguments, 'account_id' ) && + ( !structKeyExists( arguments, 'params' ) || !structKeyExists( arguments.params, 'account_id' ) ) + ) + ) { + // special case return `/account` instead of default since no account id was supplied + var methodMetadata = getBaseMethodMetadata(); + methodMetadata.path = '/account'; + methodMetadata.pathArgs = [ ]; + methodMetadata.positionalArgs = [ ]; + return stripe.call( 'accounts', 'retrieve', arguments, methodMetadata ); + } + return stripe.call( 'accounts', 'retrieve', arguments, metadata.methods.retrieve ); + } + +} diff --git a/lib/resources/applePayDomains.cfc b/lib/resources/applePayDomains.cfc new file mode 100644 index 0000000..d66b82a --- /dev/null +++ b/lib/resources/applePayDomains.cfc @@ -0,0 +1,22 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + httpMethod: 'post', + path: '/apple_pay/domains' + }, + 'delete': { + httpMethod: 'delete', + path: '/apple_pay/domains/{apple_pay_domain_id}' + }, + 'list': { + path: '/apple_pay/domains' + }, + 'retrieve': { + path: '/apple_pay/domains/{apple_pay_domain_id}' + } + } + }; + +} diff --git a/lib/resources/applicationFees.cfc b/lib/resources/applicationFees.cfc new file mode 100644 index 0000000..7c070c4 --- /dev/null +++ b/lib/resources/applicationFees.cfc @@ -0,0 +1,34 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'createRefund': { + arguments: { + amount: 'currency' + }, + httpMethod: 'post', + path: '/application_fees/{application_fee_id}/refunds' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/application_fees' + }, + 'listRefunds': { + path: '/application_fees/{application_fee_id}/refunds' + }, + 'retrieve': { + path: '/application_fees/{application_fee_id}' + }, + 'retrieveRefund': { + path: '/application_fees/{application_fee_id}/refunds/{fee_refund_id}' + }, + 'updateRefund': { + httpMethod: 'post', + path: '/application_fees/{application_fee_id}/refunds/{fee_refund_id}' + } + } + }; + +} diff --git a/lib/resources/balance.cfc b/lib/resources/balance.cfc new file mode 100644 index 0000000..2e9d262 --- /dev/null +++ b/lib/resources/balance.cfc @@ -0,0 +1,21 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'listTransactions': { + arguments: { + available_on: 'datefilter', + created: 'datefilter' + }, + path: '/balance/history' + }, + 'retrieve': { + path: '/balance' + }, + 'retrieveTransaction': { + path: '/balance/history/{balance_transaction_id}' + } + } + }; + +} diff --git a/lib/resources/charges.cfc b/lib/resources/charges.cfc new file mode 100644 index 0000000..7068c86 --- /dev/null +++ b/lib/resources/charges.cfc @@ -0,0 +1,54 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'capture': { + arguments: { + amount: 'currency', + application_fee: 'currency', + destination: { + amount: 'currency' + } + }, + httpMethod: 'post', + path: '/charges/{charge_id}/capture' + }, + 'create': { + arguments: { + amount: 'currency', + application_fee: 'currency', + currency: 'iso_currency_code', + destination: { + amount: 'currency' + } + }, + httpMethod: 'post', + path: '/charges' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/charges' + }, + 'retrieve': { + path: '/charges/{charge_id}' + }, + 'update': { + httpMethod: 'post', + path: '/charges/{charge_id}' + } + } + }; + + public struct function markAsSafe() { + var argOverrides = { fraud_details: { user_report: 'safe' } }; + return stripe.call( 'charges', 'markAsSafe', arguments, metadata.methods.update, argOverrides ); + } + + public struct function markAsFraudulent() { + var argOverrides = { fraud_details: { user_report: 'fraudulent' } }; + return stripe.call( 'charges', 'markAsFraudulent', arguments, metadata.methods.update, argOverrides ); + } + +} diff --git a/lib/resources/countrySpecs.cfc b/lib/resources/countrySpecs.cfc new file mode 100644 index 0000000..4092ac6 --- /dev/null +++ b/lib/resources/countrySpecs.cfc @@ -0,0 +1,14 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'list': { + path: '/country_specs' + }, + 'retrieve': { + path: '/country_specs/{country_spec_id}' + } + } + }; + +} diff --git a/lib/resources/coupons.cfc b/lib/resources/coupons.cfc new file mode 100644 index 0000000..41f0bc4 --- /dev/null +++ b/lib/resources/coupons.cfc @@ -0,0 +1,25 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + httpMethod: 'post', + path: '/coupons' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/coupons' + }, + 'retrieve': { + path: '/coupons/{coupon_id}' + }, + 'update': { + httpMethod: 'post', + path: '/coupons/{coupon_id}' + } + } + }; + +} diff --git a/lib/resources/customers.cfc b/lib/resources/customers.cfc new file mode 100644 index 0000000..b254790 --- /dev/null +++ b/lib/resources/customers.cfc @@ -0,0 +1,67 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + account_balance: 'currency' + }, + httpMethod: 'post', + path: '/customers' + }, + 'createSource': { + arguments: { + created: 'datefilter' + }, + httpMethod: 'post', + path: '/customers/{customer_id}/sources' + }, + 'delete': { + httpMethod: 'delete', + path: '/customers/{customer_id}' + }, + 'deleteDiscount': { + httpMethod: 'delete', + path: '/customers/{customer_id}/discount' + }, + 'deleteSource': { + httpMethod: 'delete', + path: '/customers/{customer_id}/sources/{source_id}' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/customers' + }, + 'listSources': { + path: '/customers/{customer_id}/sources' + }, + 'retrieve': { + path: '/customers/{customer_id}' + }, + 'retrieveSource': { + path: '/customers/{customer_id}/sources/{source_id}' + }, + 'update': { + arguments: { + account_balance: 'currency' + }, + httpMethod: 'post', + path: '/customers/{customer_id}' + }, + 'updateSource': { + httpMethod: 'post', + path: '/customers/{customer_id}/sources/{source_id}' + }, + 'verifySource': { + arguments: { + amounts: 'currency' + }, + httpMethod: 'post', + path: '/customers/{customer_id}/sources/{source_id}/verify' + } + } + }; + +} diff --git a/lib/resources/disputes.cfc b/lib/resources/disputes.cfc new file mode 100644 index 0000000..e51d711 --- /dev/null +++ b/lib/resources/disputes.cfc @@ -0,0 +1,25 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'close': { + httpMethod: 'post', + path: '/disputes/{dispute_id}/close' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/disputes' + }, + 'retrieve': { + path: '/disputes/{dispute_id}' + }, + 'update': { + httpMethod: 'post', + path: '/disputes/{dispute_id}' + } + } + }; + +} diff --git a/lib/resources/ephemeralKeys.cfc b/lib/resources/ephemeralKeys.cfc new file mode 100644 index 0000000..51cd953 --- /dev/null +++ b/lib/resources/ephemeralKeys.cfc @@ -0,0 +1,16 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + httpMethod: 'post', + path: '/ephemeral_keys' + }, + 'delete': { + httpMethod: 'delete', + path: '/ephemeral_keys/{ephemeral_key_id}' + } + } + }; + +} diff --git a/lib/resources/events.cfc b/lib/resources/events.cfc new file mode 100644 index 0000000..15ba072 --- /dev/null +++ b/lib/resources/events.cfc @@ -0,0 +1,17 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/events' + }, + 'retrieve': { + path: '/events/{event_id}' + } + } + }; + +} diff --git a/lib/resources/exchangeRates.cfc b/lib/resources/exchangeRates.cfc new file mode 100644 index 0000000..82c4eb0 --- /dev/null +++ b/lib/resources/exchangeRates.cfc @@ -0,0 +1,14 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'list': { + path: '/exchange_rates' + }, + 'retrieve': { + path: '/exchange_rates/{exchange_rate_id}' + } + } + }; + +} diff --git a/lib/resources/fileUploads.cfc b/lib/resources/fileUploads.cfc new file mode 100644 index 0000000..c70d091 --- /dev/null +++ b/lib/resources/fileUploads.cfc @@ -0,0 +1,25 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + endpoint: 'uploads.stripe.com/v1', + httpMethod: 'post', + multipart: true, + path: '/files' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + endpoint: 'uploads.stripe.com/v1', + path: '/files' + }, + 'retrieve': { + endpoint: 'uploads.stripe.com/v1', + path: '/files/{file_upload_id}' + } + } + }; + +} diff --git a/lib/resources/invoiceItems.cfc b/lib/resources/invoiceItems.cfc new file mode 100644 index 0000000..001c817 --- /dev/null +++ b/lib/resources/invoiceItems.cfc @@ -0,0 +1,38 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + amount: 'currency', + currency: 'iso_currency_code', + unit_amount: 'currency' + }, + httpMethod: 'post', + path: '/invoiceitems' + }, + 'delete': { + httpMethod: 'delete', + path: '/invoiceitems/{invoiceitem_id}' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/invoiceitems' + }, + 'retrieve': { + path: '/invoiceitems/{invoiceitem_id}' + }, + 'update': { + arguments: { + amount: 'currency', + unit_amount: 'currency' + }, + httpMethod: 'post', + path: '/invoiceitems/{invoiceitem_id}' + } + } + }; + +} diff --git a/lib/resources/invoices.cfc b/lib/resources/invoices.cfc new file mode 100644 index 0000000..320e1c6 --- /dev/null +++ b/lib/resources/invoices.cfc @@ -0,0 +1,54 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + application_fee: 'currency', + due_date: 'timestamp' + }, + httpMethod: 'post', + path: '/invoices' + }, + 'list': { + arguments: { + date: 'datefilter', + due_date: 'datefilter' + }, + path: '/invoices' + }, + 'pay': { + httpMethod: 'post', + path: '/invoices/{invoice_id}/pay' + }, + 'retrieve': { + path: '/invoices/{invoice_id}' + }, + 'retrieveLines': { + arguments: { + subscription_proration_date: 'timestamp', + subscription_trial_end: 'timestamp' + }, + path: '/invoices/{invoice_id}/lines' + }, + 'retrieveUpcoming': { + arguments: { + invoice_items: { + amount: 'currency' + } + }, + path: '/invoices/{invoice_id}/upcoming' + }, + 'update': { + arguments: { + application_fee: 'currency', + due_date: 'timestamp', + proration_date: 'timestamp' + }, + httpMethod: 'post', + path: '/invoices/{invoice_id}' + } + } + }; + +} diff --git a/lib/resources/orderReturns.cfc b/lib/resources/orderReturns.cfc new file mode 100644 index 0000000..d96a2a1 --- /dev/null +++ b/lib/resources/orderReturns.cfc @@ -0,0 +1,17 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/order_returns' + }, + 'retrieve': { + path: '/order_returns/{order_return_id}' + } + } + }; + +} diff --git a/lib/resources/orders.cfc b/lib/resources/orders.cfc new file mode 100644 index 0000000..3f9e397 --- /dev/null +++ b/lib/resources/orders.cfc @@ -0,0 +1,52 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + items: { + amount: 'currency' + } + }, + httpMethod: 'post', + path: '/orders' + }, + 'list': { + arguments: { + created: 'datefilter', + status_transitions: { + canceled: 'datefilter', + fulfilled: 'datefilter', + paid: 'datefilter', + returned: 'datefilter' + } + }, + path: '/orders' + }, + 'pay': { + arguments: { + application_fee: 'currency' + }, + httpMethod: 'post', + path: '/orders/{order_id}/pay' + }, + 'retrieve': { + path: '/orders/{order_id}' + }, + 'returnOrder': { + arguments: { + items: { + amount: 'currency' + } + }, + httpMethod: 'post', + path: '/orders/{order_id}/returns' + }, + 'update': { + httpMethod: 'post', + path: '/orders/{order_id}' + } + } + }; + +} diff --git a/lib/resources/payouts.cfc b/lib/resources/payouts.cfc new file mode 100644 index 0000000..5d9d4ea --- /dev/null +++ b/lib/resources/payouts.cfc @@ -0,0 +1,33 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'cancel': { + httpMethod: 'post', + path: '/payouts/{payout_id}/cancel' + }, + 'create': { + arguments: { + currency: 'iso_currency_code' + }, + httpMethod: 'post', + path: '/payouts' + }, + 'list': { + arguments: { + arrival_date: 'datefilter', + created: 'datefilter' + }, + path: '/payouts' + }, + 'retrieve': { + path: '/payouts/{payout_id}' + }, + 'update': { + httpMethod: 'post', + path: '/payouts/{payout_id}' + } + } + }; + +} diff --git a/lib/resources/plans.cfc b/lib/resources/plans.cfc new file mode 100644 index 0000000..5782f4d --- /dev/null +++ b/lib/resources/plans.cfc @@ -0,0 +1,33 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + amount: 'currency', + currency: 'iso_currency_code' + }, + httpMethod: 'post', + path: '/plans' + }, + 'delete': { + httpMethod: 'delete', + path: '/plans/{plan_id}' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/plans' + }, + 'retrieve': { + path: '/plans/{plan_id}' + }, + 'update': { + httpMethod: 'post', + path: '/plans/{plan_id}' + } + } + }; + +} diff --git a/lib/resources/products.cfc b/lib/resources/products.cfc new file mode 100644 index 0000000..faa730d --- /dev/null +++ b/lib/resources/products.cfc @@ -0,0 +1,29 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + httpMethod: 'post', + path: '/products' + }, + 'delete': { + httpMethod: 'delete', + path: '/products/{product_id}' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/products' + }, + 'retrieve': { + path: '/products/{product_id}' + }, + 'update': { + httpMethod: 'post', + path: '/products/{product_id}' + } + } + }; + +} diff --git a/lib/resources/refunds.cfc b/lib/resources/refunds.cfc new file mode 100644 index 0000000..9c16872 --- /dev/null +++ b/lib/resources/refunds.cfc @@ -0,0 +1,22 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + httpMethod: 'post', + path: '/refunds' + }, + 'list': { + path: '/refunds' + }, + 'retrieve': { + path: '/refunds/{refund_id}' + }, + 'update': { + httpMethod: 'post', + path: '/refunds/{refund_id}' + } + } + }; + +} diff --git a/lib/resources/skus.cfc b/lib/resources/skus.cfc new file mode 100644 index 0000000..ea05d54 --- /dev/null +++ b/lib/resources/skus.cfc @@ -0,0 +1,32 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + price: 'currency' + }, + httpMethod: 'post', + path: '/skus' + }, + 'delete': { + httpMethod: 'delete', + path: '/skus/{sku_id}' + }, + 'list': { + path: '/skus' + }, + 'retrieve': { + path: '/skus/{sku_id}' + }, + 'update': { + arguments: { + price: 'currency' + }, + httpMethod: 'post', + path: '/skus/{sku_id}' + } + } + }; + +} diff --git a/lib/resources/sources.cfc b/lib/resources/sources.cfc new file mode 100644 index 0000000..68b00b1 --- /dev/null +++ b/lib/resources/sources.cfc @@ -0,0 +1,40 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + mandate: { + acceptance: { + date: 'timestamp' + } + } + }, + httpMethod: 'post', + path: '/sources' + }, + 'listSourceTransactions': { + path: '/sources/{source_id}/source_transactions' + }, + 'retrieve': { + path: '/sources/{source_id}' + }, + 'update': { + arguments: { + mandate: { + acceptance: { + date: 'timestamp' + } + } + }, + httpMethod: 'post', + path: '/sources/{source_id}' + }, + 'verify': { + httpMethod: 'post', + path: '/sources/{source_id}/verify' + } + } + }; + +} diff --git a/lib/resources/subscriptionItems.cfc b/lib/resources/subscriptionItems.cfc new file mode 100644 index 0000000..d0ab70c --- /dev/null +++ b/lib/resources/subscriptionItems.cfc @@ -0,0 +1,32 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + proration_date: 'timestamp' + }, + httpMethod: 'post', + path: '/subscription_items' + }, + 'delete': { + httpMethod: 'delete', + path: '/subscription_items/{subscription_item_id}' + }, + 'list': { + path: '/subscription_items' + }, + 'retrieve': { + path: '/subscription_items/{subscription_item_id}' + }, + 'update': { + arguments: { + proration_date: 'timestamp' + }, + httpMethod: 'post', + path: '/subscription_items/{subscription_item_id}' + } + } + }; + +} diff --git a/lib/resources/subscriptions.cfc b/lib/resources/subscriptions.cfc new file mode 100644 index 0000000..00005ea --- /dev/null +++ b/lib/resources/subscriptions.cfc @@ -0,0 +1,40 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + trial_end: 'timestamp' + }, + httpMethod: 'post', + path: '/subscriptions' + }, + 'delete': { + httpMethod: 'delete', + path: '/subscriptions/{subscription_id}' + }, + 'deleteDiscount': { + httpMethod: 'delete', + path: '/subscriptions/{subscription_id}/discount' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/subscriptions' + }, + 'retrieve': { + path: '/subscriptions/{subscription_id}' + }, + 'update': { + arguments: { + proration_date: 'timestamp', + trial_end: 'timestamp' + }, + httpMethod: 'post', + path: '/subscriptions/{subscription_id}' + } + } + }; + +} diff --git a/lib/resources/tokens.cfc b/lib/resources/tokens.cfc new file mode 100644 index 0000000..afd440a --- /dev/null +++ b/lib/resources/tokens.cfc @@ -0,0 +1,15 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + httpMethod: 'post', + path: '/tokens' + }, + 'retrieve': { + path: '/tokens/{token_id}' + } + } + }; + +} diff --git a/lib/resources/transfers.cfc b/lib/resources/transfers.cfc new file mode 100644 index 0000000..cca326f --- /dev/null +++ b/lib/resources/transfers.cfc @@ -0,0 +1,49 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + amount: 'currency', + currency: 'iso_currency_code' + }, + httpMethod: 'post', + path: '/transfers' + }, + 'createReversal': { + arguments: { + amount: 'currency' + }, + httpMethod: 'post', + path: '/transfers/{transfer_id}/reversals' + }, + 'list': { + arguments: { + created: 'datefilter' + }, + path: '/transfers' + }, + 'listReversals': { + arguments: { + limit: 'integer' + }, + path: '/transfers/{transfer_id}/reversals' + }, + 'retrieve': { + path: '/transfers/{transfer_id}' + }, + 'retrieveReversal': { + path: '/transfers/{transfer_id}/reversals/{transfer_reversal_id}' + }, + 'update': { + httpMethod: 'post', + path: '/transfers/{transfer_id}' + }, + 'updateReversal': { + httpMethod: 'post', + path: '/transfers/{transfer_id}/reversals/{transfer_reversal_id}' + } + } + }; + +} diff --git a/lib/resources/usageRecords.cfc b/lib/resources/usageRecords.cfc new file mode 100644 index 0000000..1430375 --- /dev/null +++ b/lib/resources/usageRecords.cfc @@ -0,0 +1,16 @@ +component extends="abstract.apiResource" { + + variables.metadata = { + methods: { + 'create': { + arguments: { + timestamp: 'timestamp' + }, + httpMethod: 'post', + path: '/subscription_items/{subscription_item}/usage_records', + positionalArgs: [] + } + } + }; + +} diff --git a/lib/webhooks.cfc b/lib/webhooks.cfc new file mode 100644 index 0000000..330e7ae --- /dev/null +++ b/lib/webhooks.cfc @@ -0,0 +1,79 @@ +component { + + variables.default_tolerance = 300; + variables.expected_scheme = 'v1'; + + public any function init( + required any responseParser + ) { + variables.messageDigest = createObject( 'java', 'java.security.MessageDigest' ); + variables.parserUtils = new parsers.parserUtils(); + variables.responseParser = arguments.responseParser; + return this; + } + + public struct function constructEvent( + required string payload, + required string header, + required string secret, + numeric tolerance = default_tolerance + ) { + verifyHeader( payload, header, secret, tolerance ); + var event = deserializeJSON( payload ); + responseParser.parse( event ); + return event; + } + + private void function verifyHeader( + required string payload, + required string header, + required string secret, + required numeric tolerance + ) { + var details = parseHeader( header ); + + if ( details.timestamp == -1 ) { + throw( 'Unable to extract timestamp and signatures from header' ); + } + + if ( !arrayLen( details.signatures ) ) { + throw( 'No signatures found with expected scheme' ); + } + + var expectedSignature = hmac( details.timestamp & '.' & payload, secret, 'hmacSHA256', 'utf-8' ).lcase(); + + var validSignatures = arrayFilter( details.signatures, function( s ) { + return MessageDigest.isEqual( s.getBytes(), expectedSignature.getBytes() ); + } ); + + if ( !arrayLen( validSignatures ) ) { + throw( 'No signatures found matching the expected signature for payload.' ); + } + + var timestampAge = parserUtils.getUTCTimestamp( now() ) - details.timestamp; + + if ( tolerance > 0 && timestampAge > tolerance ) { + throw( 'Timestamp outside the tolerance zone' ); + } + } + + private struct function parseHeader( + required string header + ) { + return arrayReduce( listToArray( header ), function( parsed, item ) { + var key = listFirst( item, '=' ); + var value = listRest( item, '=' ); + + if ( key == 't' ) { + parsed.timestamp = value; + } + + if ( key == variables.expected_scheme ) { + parsed.signatures.append( value ); + } + + return parsed; + }, { timestamp: -1, signatures: [ ] } ); + } + +} diff --git a/metadata/account.json b/metadata/account.json new file mode 100644 index 0000000..f1cf9f0 --- /dev/null +++ b/metadata/account.json @@ -0,0 +1,9 @@ +{ + "created": "datetime", + "tos_acceptance": { + "date": "datetime" + }, + "verification": { + "due_by": "datetime" + } +} diff --git a/metadata/application_fee.json b/metadata/application_fee.json new file mode 100644 index 0000000..c779b68 --- /dev/null +++ b/metadata/application_fee.json @@ -0,0 +1,5 @@ +{ + "amount": "currency", + "amount_refunded": "currency", + "created": "datetime" +} diff --git a/metadata/balance.json b/metadata/balance.json new file mode 100644 index 0000000..f3135b2 --- /dev/null +++ b/metadata/balance.json @@ -0,0 +1,17 @@ +{ + "available": { + "amount": "currency", + "source_types": { + "card": "currency" + } + }, + "connect_reserved": { + "amount": "currency" + }, + "pending": { + "amount": "currency", + "source_types": { + "card": "currency" + } + } +} diff --git a/metadata/balance_transaction.json b/metadata/balance_transaction.json new file mode 100644 index 0000000..c72c2f2 --- /dev/null +++ b/metadata/balance_transaction.json @@ -0,0 +1,10 @@ +{ + "amount": "currency", + "available_on": "datetime", + "created": "datetime", + "fee": "currency", + "fee_details": { + "amount": "currency" + }, + "net": "currency" +} diff --git a/metadata/charge.json b/metadata/charge.json new file mode 100644 index 0000000..c779b68 --- /dev/null +++ b/metadata/charge.json @@ -0,0 +1,5 @@ +{ + "amount": "currency", + "amount_refunded": "currency", + "created": "datetime" +} diff --git a/metadata/coupon.json b/metadata/coupon.json new file mode 100644 index 0000000..9bcb249 --- /dev/null +++ b/metadata/coupon.json @@ -0,0 +1,4 @@ +{ + "amount_off": "currency", + "created": "datetime" +} diff --git a/metadata/discount.json b/metadata/discount.json new file mode 100644 index 0000000..842f643 --- /dev/null +++ b/metadata/discount.json @@ -0,0 +1,4 @@ +{ + "start": "datetime", + "end": "datetime" +} diff --git a/metadata/dispute.json b/metadata/dispute.json new file mode 100644 index 0000000..5ed9b87 --- /dev/null +++ b/metadata/dispute.json @@ -0,0 +1,7 @@ +{ + "amount": "currency", + "created": "datetime", + "evidence_details": { + "due_by": "timestamp" + } +} diff --git a/metadata/ephemeral_key.json b/metadata/ephemeral_key.json new file mode 100644 index 0000000..ed8057c --- /dev/null +++ b/metadata/ephemeral_key.json @@ -0,0 +1,4 @@ +{ + "created": "datetime", + "expires": "datetime" +} diff --git a/metadata/fee_refund.json b/metadata/fee_refund.json new file mode 100644 index 0000000..7835f49 --- /dev/null +++ b/metadata/fee_refund.json @@ -0,0 +1,4 @@ +{ + "amount": "currency", + "created": "datetime" +} diff --git a/metadata/invoice.json b/metadata/invoice.json new file mode 100644 index 0000000..5dc0c27 --- /dev/null +++ b/metadata/invoice.json @@ -0,0 +1,15 @@ +{ + "amount_due": "currency", + "application_fee": "currency", + "date": "datetime", + "due_date": "datetime", + "ending_balance": "currency", + "next_payment_attempt": "datetime", + "period_end": "datetime", + "period_start": "datetime", + "starting_balance": "currency", + "subscription_proration_date": "datetime", + "subtotal": "currency", + "tax": "currency", + "total": "currency" +} diff --git a/metadata/invoiceitem.json b/metadata/invoiceitem.json new file mode 100644 index 0000000..719c9ab --- /dev/null +++ b/metadata/invoiceitem.json @@ -0,0 +1,5 @@ +{ + "amount": "currency", + "date": "datetime", + "proration": "boolean" +} diff --git a/metadata/line_item.json b/metadata/line_item.json new file mode 100644 index 0000000..531aa8d --- /dev/null +++ b/metadata/line_item.json @@ -0,0 +1,7 @@ +{ + "amount": "currency", + "period": { + "end": "datetime", + "start": "datetime" + } +} diff --git a/metadata/order.json b/metadata/order.json new file mode 100644 index 0000000..8d0fce1 --- /dev/null +++ b/metadata/order.json @@ -0,0 +1,7 @@ +{ + "amount": "currency", + "amount_returned": "currency", + "application_fee": "currency", + "created": "datetime", + "updated": "datetime" +} diff --git a/metadata/order_item.json b/metadata/order_item.json new file mode 100644 index 0000000..7e0249e --- /dev/null +++ b/metadata/order_item.json @@ -0,0 +1,3 @@ +{ + "amount": "currency" +} diff --git a/metadata/order_return.json b/metadata/order_return.json new file mode 100644 index 0000000..7835f49 --- /dev/null +++ b/metadata/order_return.json @@ -0,0 +1,4 @@ +{ + "amount": "currency", + "created": "datetime" +} diff --git a/metadata/payout.json b/metadata/payout.json new file mode 100644 index 0000000..b1897d8 --- /dev/null +++ b/metadata/payout.json @@ -0,0 +1,5 @@ +{ + "amount": "currency", + "arrival_date": "datetime", + "created": "datetime" +} diff --git a/metadata/plan.json b/metadata/plan.json new file mode 100644 index 0000000..7835f49 --- /dev/null +++ b/metadata/plan.json @@ -0,0 +1,4 @@ +{ + "amount": "currency", + "created": "datetime" +} diff --git a/metadata/product.json b/metadata/product.json new file mode 100644 index 0000000..a08fe3a --- /dev/null +++ b/metadata/product.json @@ -0,0 +1,4 @@ +{ + "updated": "datetime", + "created": "datetime" +} diff --git a/metadata/refund.json b/metadata/refund.json new file mode 100644 index 0000000..7835f49 --- /dev/null +++ b/metadata/refund.json @@ -0,0 +1,4 @@ +{ + "amount": "currency", + "created": "datetime" +} diff --git a/metadata/return.json b/metadata/return.json new file mode 100644 index 0000000..7835f49 --- /dev/null +++ b/metadata/return.json @@ -0,0 +1,4 @@ +{ + "amount": "currency", + "created": "datetime" +} diff --git a/metadata/sku.json b/metadata/sku.json new file mode 100644 index 0000000..f544982 --- /dev/null +++ b/metadata/sku.json @@ -0,0 +1,5 @@ +{ + "created": "datetime", + "price": "currency", + "updated": "datetime" +} diff --git a/metadata/source.json b/metadata/source.json new file mode 100644 index 0000000..7835f49 --- /dev/null +++ b/metadata/source.json @@ -0,0 +1,4 @@ +{ + "amount": "currency", + "created": "datetime" +} diff --git a/metadata/subscription.json b/metadata/subscription.json new file mode 100644 index 0000000..5ca4a26 --- /dev/null +++ b/metadata/subscription.json @@ -0,0 +1,10 @@ +{ + "canceled_at": "datetime", + "created": "datetime", + "current_period_end": "datetime", + "current_period_start": "datetime", + "ended_at": "datetime", + "start": "datetime", + "trial_end": "datetime", + "trial_start": "datetime" +} diff --git a/metadata/transfer.json b/metadata/transfer.json new file mode 100644 index 0000000..63d131b --- /dev/null +++ b/metadata/transfer.json @@ -0,0 +1,5 @@ +{ + "amount": "currency", + "amount_reversed": "currency", + "created": "datetime" +} diff --git a/metadata/transfer_reversal.json b/metadata/transfer_reversal.json new file mode 100644 index 0000000..7835f49 --- /dev/null +++ b/metadata/transfer_reversal.json @@ -0,0 +1,4 @@ +{ + "amount": "currency", + "created": "datetime" +} diff --git a/reference.md b/reference.md new file mode 100644 index 0000000..8829d93 --- /dev/null +++ b/reference.md @@ -0,0 +1,278 @@ +# stripe-cfml reference + +For documentation on which arguments can be passed to each method please see the [Stripe API Reference](https://stripe.com/docs/api). The arguments that are listed here are named arguments that ultimately appear in the URL of an API HTTP request. Since the Node library passes these arguments by position, you can refer to this page to see the names to use when passing these arguments by name. + +## stripe.accounts + +```cfc +stripe.accounts.create(); +stripe.accounts.createExternalAccount(account_id); +stripe.accounts.createLoginLink(account_id); +stripe.accounts.delete(account_id); +stripe.accounts.deleteExternalAccount(account_id, source_id); +stripe.accounts.list(); +stripe.accounts.listExternalAccounts(account_id); +stripe.accounts.reject(account_id); +stripe.accounts.retrieve(); +stripe.accounts.retrieve(account_id); +stripe.accounts.retrieveExternalAccount(account_id, source_id); +stripe.accounts.update(account_id); +stripe.accounts.updateExternalAccount(account_id, source_id); +``` + +## stripe.applePayDomains + +```cfc +stripe.applePayDomains.create(); +stripe.applePayDomains.delete(apple_pay_domain_id); +stripe.applePayDomains.list(); +stripe.applePayDomains.retrieve(apple_pay_domain_id); +``` + +## stripe.applicationFees + +```cfc +stripe.applicationFees.createRefund(application_fee_id); +stripe.applicationFees.list(); +stripe.applicationFees.listRefunds(application_fee_id); +stripe.applicationFees.retrieve(application_fee_id); +stripe.applicationFees.retrieveRefund(application_fee_id, fee_refund_id); +stripe.applicationFees.updateRefund(application_fee_id, fee_refund_id); +``` + +## stripe.balance + +```cfc +stripe.balance.listTransactions(); +stripe.balance.retrieve(); +stripe.balance.retrieveTransaction(balance_transaction_id); +``` + +## stripe.charges + +```cfc +stripe.charges.capture(charge_id); +stripe.charges.create(); +stripe.charges.list(); +stripe.charges.retrieve(charge_id); +stripe.charges.update(charge_id); +stripe.charges.markAsSafe(charge_id); +stripe.charges.markAsFraudulent(charge_id); +``` + +## stripe.countrySpecs + +```cfc +stripe.countrySpecs.list(); +stripe.countrySpecs.retrieve(country_spec_id); +``` + +## stripe.coupons + +```cfc +stripe.coupons.create(); +stripe.coupons.list(); +stripe.coupons.retrieve(coupon_id); +stripe.coupons.update(coupon_id); +``` + +## stripe.customers + +```cfc +stripe.customers.create(); +stripe.customers.createSource(customer_id); +stripe.customers.delete(customer_id); +stripe.customers.deleteDiscount(customer_id); +stripe.customers.deleteSource(customer_id, source_id); +stripe.customers.list(); +stripe.customers.listSources(customer_id); +stripe.customers.retrieve(customer_id); +stripe.customers.retrieveSource(customer_id, source_id); +stripe.customers.update(customer_id); +stripe.customers.updateSource(customer_id, source_id); +stripe.customers.verifySource(customer_id, source_id); +``` + +## stripe.disputes + +```cfc +stripe.disputes.close(dispute_id); +stripe.disputes.list(); +stripe.disputes.retrieve(dispute_id); +stripe.disputes.update(dispute_id); +``` + +## stripe.ephemeralKeys + +```cfc +stripe.ephemeralKeys.create(); +stripe.ephemeralKeys.delete(ephemeral_key_id); +``` + +## stripe.events + +```cfc +stripe.events.list(); +stripe.events.retrieve(event_id); +``` + +## stripe.exchangeRates + +```cfc +stripe.exchangeRates.list(); +stripe.exchangeRates.retrieve(exchange_rate_id); +``` + +## stripe.fileUploads + +```cfc +stripe.fileUploads.create(); +stripe.fileUploads.list(); +stripe.fileUploads.retrieve(file_upload_id); +``` + +## stripe.invoiceItems + +```cfc +stripe.invoiceItems.create(); +stripe.invoiceItems.delete(invoiceitem_id); +stripe.invoiceItems.list(); +stripe.invoiceItems.retrieve(invoiceitem_id); +stripe.invoiceItems.update(invoiceitem_id); +``` + +## stripe.invoices + +```cfc +stripe.invoices.create(); +stripe.invoices.list(); +stripe.invoices.pay(invoice_id); +stripe.invoices.retrieve(invoice_id); +stripe.invoices.retrieveLines(invoice_id); +stripe.invoices.retrieveUpcoming(invoice_id); +stripe.invoices.update(invoice_id); +``` + +## stripe.orderReturns + +```cfc +stripe.orderReturns.list(); +stripe.orderReturns.retrieve(order_return_id); +``` + +## stripe.orders + +```cfc +stripe.orders.create(); +stripe.orders.list(); +stripe.orders.pay(order_id); +stripe.orders.retrieve(order_id); +stripe.orders.returnOrder(order_id); +stripe.orders.update(order_id); +``` + +## stripe.payouts + +```cfc +stripe.payouts.cancel(payout_id); +stripe.payouts.create(); +stripe.payouts.list(); +stripe.payouts.retrieve(payout_id); +stripe.payouts.update(payout_id); +``` + +## stripe.plans + +```cfc +stripe.plans.create(); +stripe.plans.delete(plan_id); +stripe.plans.list(); +stripe.plans.retrieve(plan_id); +stripe.plans.update(plan_id); +``` + +## stripe.products + +```cfc +stripe.products.create(); +stripe.products.delete(product_id); +stripe.products.list(); +stripe.products.retrieve(product_id); +stripe.products.update(product_id); +``` + +## stripe.refunds + +```cfc +stripe.refunds.create(); +stripe.refunds.list(); +stripe.refunds.retrieve(refund_id); +stripe.refunds.update(refund_id); +``` + +## stripe.skus + +```cfc +stripe.skus.create(); +stripe.skus.delete(sku_id); +stripe.skus.list(); +stripe.skus.retrieve(sku_id); +stripe.skus.update(sku_id); +``` + +## stripe.sources + +```cfc +stripe.sources.create(); +stripe.sources.listSourceTransactions(source_id); +stripe.sources.retrieve(source_id); +stripe.sources.update(source_id); +stripe.sources.verify(source_id); +``` + +## stripe.subscriptionItems + +```cfc +stripe.subscriptionItems.create(); +stripe.subscriptionItems.delete(subscription_item_id); +stripe.subscriptionItems.list(); +stripe.subscriptionItems.retrieve(subscription_item_id); +stripe.subscriptionItems.update(subscription_item_id); +``` + +## stripe.subscriptions + +```cfc +stripe.subscriptions.create(); +stripe.subscriptions.delete(subscription_id); +stripe.subscriptions.deleteDiscount(subscription_id); +stripe.subscriptions.list(); +stripe.subscriptions.retrieve(subscription_id); +stripe.subscriptions.update(subscription_id); +``` + +## stripe.tokens + +```cfc +stripe.tokens.create(); +stripe.tokens.retrieve(token_id); +``` + +## stripe.transfers + +```cfc +stripe.transfers.create(); +stripe.transfers.createReversal(transfer_id); +stripe.transfers.list(); +stripe.transfers.listReversals(transfer_id); +stripe.transfers.retrieve(transfer_id); +stripe.transfers.retrieveReversal(transfer_id, transfer_reversal_id); +stripe.transfers.update(transfer_id); +stripe.transfers.updateReversal(transfer_id, transfer_reversal_id); +``` + +## stripe.usageRecords + +```cfc +stripe.usageRecords.create(); +``` diff --git a/server.json b/server.json new file mode 100644 index 0000000..495e61a --- /dev/null +++ b/server.json @@ -0,0 +1,10 @@ +{ + "app":{ + "cfengine":"adobe@2016" + }, + "web":{ + "http":{ + "port":"45000" + } + } +} \ No newline at end of file diff --git a/stripe.cfc b/stripe.cfc index 453a0c9..79353ba 100644 --- a/stripe.cfc +++ b/stripe.cfc @@ -1,680 +1,149 @@ -/* - Copyright (c) 2013-2015, John Berquist - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -component { - - variables.integerFields = [ "quantity","limit","interval_count","trial_period_days","duration_in_months","max_redemptions", "percent_off","exp_month","exp_year","cvc" ]; - variables.numericFields = [ "application_fee_percent","tax_percent" ]; - variables.currencyFields = [ "amount","amount_refunded","amount_off","account_balance","fee","application_fee","net" ]; - variables.timestampFields = [ "created","trial_start","trial_end","redeem_by","start","end","ended_at","period_start","period_end","date","next_payment_attempt","available_on","current_period_start","current_period_end","canceled_at","gt","gte","lt","lte" ]; - variables.booleanFields = [ "active","at_period_end","capture","closed","debit_negative_balances","discountable","filled","forgiven","prorate","refund_application_fee","refund_mispayments","managed","uncaptured_funds" ]; - variables.arrayFields = [ "expand","include","additional_owners" ]; - variables.dictionaryFields = { - source = { required = [ "object","number","exp_month","exp_year" ], optional = [ "cvc","name","address_line1","address_line2","address_city","address_zip","address_state","address_country" ] }, - card = { required = [ "number","exp_month","exp_year" ], optional = [ "cvc","name","address_line1","address_line2","address_city","address_zip","address_state","address_country" ] }, - evidence = { required = [ ], optional = [ "access_activity_log","billing_address","cancellation_policy","cancellation_policy_disclosure","cancellation_rebuttal","customer_communication","customer_email_address","customer_name","customer_purchase_ip","customer_signature","duplicate_charge_documentation","duplicate_charge_explanation","duplicate_charge_id","product_description","receipt","refund_policy","refund_policy_disclosure","refund_refusal_explanation","service_date","service_documentation","shipping_address","shipping_carrier","shipping_date","shipping_documentation","shipping_tracking_number","uncategorized_file","uncategorized_text" ] }, - bank_account = { required = [ "country","currentcy","account_number" ], optional = [ "routing_number" ] }, - legal_entity = { required = [ "" ], optional = [ "type","address","business_name","business_tax_id","business_vat_id","dob","first_name","last_name","personal_address","personal_id_number","ssn_last_4","verification","additional_owners" ] }, - dob = { required = [ "day","month","year" ], optional = [ ] }, - personal_address = { required = [ ], optional = [ "line1","line2","city","state","postal_code","country" ] }, - verification = { required = [ "" ], optional = [ "document" ] }, - tos_acceptance = { required = [ "date","ip" ], optional = [ "user_agent" ] }, - transfer_schedule = { required = [ "" ], optional = [ "delay_days","interval","monthly_anchor","weekly_anchor" ] }, - available_on = { required = [ ], optional = [ "gt","gte","lt","lte" ] }, - created = { required = [ ], optional = [ "gt","gte","lt","lte" ] }, - date = { required = [ ], optional = [ "gt","gte","lt","lte" ] }, - shipping = { required = [ ], optional = [ "address","carrier","phone","name","tracking_number" ] }, - address = { required = [ ], optional = [ "line1","line2","city","state","postal_code","country" ] }, - metadata = { required = [ ], optional = [ ] } - }; - - public any function init( required string apiKey, boolean convertUTCTimestamps = true, boolean convertToCents = false, string defaultCurrency = "usd", boolean includeRaw = false, string apiBaseUrl = "https://api.stripe.com/v1/", string fileUploadBaseUrl = "https://uploads.stripe.com/v1/" ) { - structAppend( variables, arguments ); - - variables.utcBaseDate = dateAdd( "l", createDate( 1970,1,1 ).getTime() * -1, createDate( 1970,1,1 ) ); - - return this; - } - - // Charges - - public struct function createCharge( required numeric amount, string currency = variables.defaultCurrency, numeric application_fee, boolean capture, string customer, string description, string destination, struct metadata, string receipt_email, struct shipping, any source, string statement_descriptor ) { - return apiCall( "charges", setupParams( arguments ), "post" ); - } - - public struct function getCharge( required string id ) { - return apiCall( "charges/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateCharge( required string id, string description, struct metadata ) { - return apiCall( "charges/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function captureCharge( required string id, numeric amount, numeric application_fee ) { - return apiCall( "charges/#trim( arguments.id )#/capture", setupParams( arguments ), "post" ); - } - - public struct function listCharges( any created, string customer, string ending_before, numeric limit, string starting_after ) { - return apiCall( "charges", setupParams( arguments ) ); - } - - // Refunds - - public struct function createChargeRefund( required string id, numeric amount, struct metadata, string reason, boolean refund_application_fee ) { - return apiCall( "charges/#trim( arguments.id )#/refunds", setupParams( arguments ), "post" ); - } - - public struct function getChargeRefund( required string charge_id, required string id ) { - return apiCall( "charges/#trim( arguments.charge_id )#/refunds/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateChargeRefund( required string charge_id, required string id, struct metadata ) { - return apiCall( "charges/#trim( arguments.charge_id )#/refunds/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function listChargeRefunds( required string charge_id, string ending_before, numeric limit, string starting_after ) { - return apiCall( "charges/#trim( arguments.charge_id )#/refunds", setupParams( arguments ) ); - } - - // Customers - - public struct function createCustomer( numeric account_balance, string coupon, string description, string email, struct metadata, string plan, numeric quantity, any source, any trial_end ) { - return apiCall( "customers", setupParams( arguments ), "post" ); - } - - public struct function getCustomer( required string id ) { - return apiCall( "customers/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateCustomer( required string id, numeric account_balance, string coupon, string default_source, string description, string email, struct metadata, any source ) { - return apiCall( "customers/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function deleteCustomer( required string id ) { - return apiCall( "customers/#trim( arguments.id )#", setupParams( arguments ), "delete" ); - } - - public struct function listCustomers( any created, string ending_before, numeric limit, string starting_after ) { - return apiCall( "customers", setupParams( arguments ) ); - } - - // Subscriptions - - public struct function createCustomerSubscription( required string customer_id, required string plan, numeric application_fee_percent, string coupon, struct metadata, boolean prorate, numeric quantity, any source, numeric tax_percent, any trial_end ) { - return apiCall( "customers/#trim( arguments.customer_id )#/subscriptions", setupParams( arguments ), "post" ); - } - - public struct function getCustomerSubscription( required string customer_id, required string id ) { - return apiCall( "customers/#trim( arguments.customer_id )#/subscriptions/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateCustomerSubscription( required string customer_id, required string id, numeric application_fee_percent, string coupon, struct metadata, string plan, boolean prorate, numeric quantity, any source, numeric tax_percent, any trial_end ) { - return apiCall( "customers/#trim( arguments.customer_id )#/subscriptions/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function cancelCustomerSubscription( required string customer_id, required string id, boolean at_period_end ) { - return apiCall( "customers/#trim( arguments.customer_id )#/subscriptions/#trim( arguments.id )#", setupParams( arguments ), "delete" ); - } - - public struct function listCustomerSubscriptions( required string customer_id, string ending_before, numeric limit, string starting_after ) { - return apiCall( "customers/#trim( arguments.customer_id )#/subscriptions", setupParams( arguments ) ); - } - - // Cards / Sources - - public struct function createCustomerSource( required string customer_id, any source ) { - return apiCall( "customers/#trim( arguments.customer_id )#/sources", setupParams( arguments ), "post" ); - } - - public struct function getCustomerSource( required string customer_id, required string id ) { - return apiCall( "customers/#trim( arguments.customer_id )#/sources/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateCustomerSource( required string customer_id, required string id, string address_city, string address_country, string address_line1, string address_line2, string address_state, string address_zip, numeric exp_month, numeric exp_year, struct metadata, string name ) { - return apiCall( "customers/#trim( arguments.customer_id )#/sources/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function deleteCustomerSource( required string customer_id, required string id ) { - return apiCall( "customers/#trim( arguments.customer_id )#/sources/#trim( arguments.id )#", setupParams( arguments ), "delete" ); - } - - public struct function listCustomerSources( required string customer_id, string ending_before, numeric limit, string starting_after ) { - return apiCall( "customers/#trim( arguments.customer_id )#/sources", setupParams( arguments ) ); - } - - // Plans - - public struct function createPlan( required string id, required numeric amount, string currency = variables.defaultCurrency, required string interval, required string name, numeric interval_count, struct metadata, string statement_descriptor, numeric trial_period_days ) { - return apiCall( "plans", setupParams( arguments, true ), "post" ); - } - - public struct function getPlan( required string id ) { - return apiCall( "plans/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updatePlan( required string id, struct metadata, string name, string statement_descriptor ) { - return apiCall( "plans/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function deletePlan( required string id ) { - return apiCall( "plans/#trim( arguments.id )#", setupParams( arguments ), "delete" ); - } - - public struct function listPlans( any created, string ending_before, numeric limit, string starting_after ) { - return apiCall( "plans", setupParams( arguments ) ); - } - - // Coupons - - public struct function createCoupon( required string duration, numeric amount_off, string currency = variables.defaultCurrency, numeric duration_in_months, string id, numeric max_redemptions, struct metadata, numeric percent_off, any redeem_by ) { - // only one of percent_off and amount_off can be provided to the createCoupon function - if ( !( structKeyExists( arguments, "amount_off" ) xor structKeyExists( arguments, "percent_off" ) ) ) { - throwError( "createCoupon: please provide one and only one of amount_off and percent_off params" ); - } - // duration_in_months is required only when duration equals "repeating" - if ( arguments.duration == "repeating" xor structKeyExists( arguments, "duration_in_months" ) ) { - throwError( "createCoupon: duration_in_months is required when and only when duration equals 'repeating'" ); - } - return apiCall( "coupons", setupParams( arguments, true ), "post" ); - } - - public struct function getCoupon( required string id ) { - return apiCall( "coupons/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateCoupon( required string id, struct metadata ) { - return apiCall( "coupons/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function deleteCoupon( required string id ) { - return apiCall( "coupons/#trim( arguments.id )#", setupParams( arguments ), "delete" ); - } - - public struct function listCoupons( any created, string ending_before, numeric limit, string starting_after ) { - return apiCall( "coupons", setupParams( arguments ) ); - } - - // Discounts - - public struct function deleteCustomerDiscount( required string id ) { - return apiCall( "customers/#trim( arguments.id )#/discount", setupParams( arguments ), "delete" ); - } - - public struct function deleteCustomerSubscriptionDiscount( required string customer_id, required string id ) { - return apiCall( "customers/#trim( arguments.customer_id )#/subscriptions/#trim( arguments.id )#/discount", setupParams( arguments ), "delete" ); - } - - // Invoices - - public struct function createInvoice( required string customer, numeric application_fee, string description, struct metadata, string statement_descriptor, string subscription, numeric tax_percent ) { - return apiCall( "invoices", setupParams( arguments ), "post" ); - } - - public struct function getInvoice( required string id ) { - return apiCall( "invoices/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function getInvoiceLineItems( required string id, string customer, string ending_before, numeric limit, string starting_after, string subscription ) { - return apiCall( "invoices/#trim( arguments.id )#/lines", setupParams( arguments ) ); - } - - public struct function getUpcomingInvoice( required string customer, string subscription ) { - return apiCall( "invoices/upcoming", setupParams( arguments ) ); - } - - public struct function updateInvoice( required string id, boolean closed, string description, boolean forgiven, struct metadata, string statement_descriptor, numeric tax_percent ) { - return apiCall( "invoices/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function payInvoice( required string id ) { - return apiCall( "invoices/#trim( arguments.id )#/pay", setupParams( arguments ), "post" ); - } - - public struct function listInvoices( string customer, any date, string ending_before, numeric limit, string starting_after ) { - return apiCall( "invoices", setupParams( arguments ) ); - } - - // Invoice Items - - public struct function createInvoiceItem( required string customer, required numeric amount, string currency = variables.defaultCurrency, string description, boolean discountable, string invoice, struct metadata, string subscription ) { - return apiCall( "invoiceitems", setupParams( arguments ), "post" ); - } - - public struct function getInvoiceItem( required string id ) { - return apiCall( "invoiceitems/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateInvoiceItem( required string id, numeric amount, string description, boolean discountable, struct metadata ) { - return apiCall( "invoiceitems/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function deleteInvoiceItem( required string id ) { - return apiCall( "invoiceitems/#trim( arguments.id )#", setupParams( arguments ), "delete" ); - } - - public struct function listInvoiceItems( any created, string customer, string ending_before, numeric limit, string starting_after ) { - return apiCall( "invoiceitems", setupParams( arguments ) ); - } - - // Disputes - - public struct function updateChargeDispute( required string id, struct evidence, struct metadata ) { - return apiCall( "charges/#trim( arguments.id )#/dispute", setupParams( arguments ), "post" ); - } - - public struct function closeChargeDispute( required string id ) { - return apiCall( "charges/#trim( arguments.id )#/dispute/close", setupParams( arguments ), "post" ); - } - - // Transfers - - public struct function createTransfer( required numeric amount, string currency = variables.defaultCurrency, required string destination, string description, struct metadata, string source_transaction, string statement_descriptor ) { - return apiCall( "transfers", setupParams( arguments ), "post" ); - } - - public struct function getTransfer( required string id ) { - return apiCall( "transfers/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateTransfer( required string id, string description, struct metadata ) { - return apiCall( "transfers/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function listTransfers( any created, any date, string ending_before, numeric limit, string recipient, string starting_after, string status ) { - return apiCall( "transfers", setupParams( arguments ) ); - } - - // Transfer Reversals - - public struct function createTransferReversal( required string transfer_id, numeric amount, string description, struct metadata, boolean refund_application_fee ) { - return apiCall( "transfers/#trim( arguments.transfer_id )#/reversals", setupParams( arguments ), "post" ); - } - - public struct function getTransferReversal( required string transfer_id, required string id ) { - return apiCall( "transfers/#trim( arguments.transfer_id )#/reversals/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateTransferReversal( required string transfer_id, required string id, string description, struct metadata ) { - return apiCall( "transfers/#trim( arguments.transfer_id )#/reversals/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function listTransferReversals( required string transfer_id, string ending_before, numeric limit, string starting_after ) { - return apiCall( "transfers/#trim( arguments.transfer_id )#/reversals", setupParams( arguments ) ); - } - - // Recipients - Deprecated - - /* - public struct function createRecipient( required string name, required string type, any bank_account, string email, string description, struct metadata, string tax_id ) { - return apiCall( "recipients", setupParams( arguments ), "post" ); - } - - public struct function getRecipient( required string id ) { - return apiCall( "recipients/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateRecipient( required string id, any bank_account, string email, string description, struct metadata, string name, string tax_id ) { - return apiCall( "recipients/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function deleteRecipient( required string id ) { - return apiCall( "recipients/#trim( arguments.id )#", setupParams( arguments ), "delete" ); - } - - public struct function listRecipients( string ending_before, numeric limit, string starting_after, boolean verified ) { - return apiCall( "recipients", setupParams( arguments ) ); - } - */ - - // Application Fees - - public struct function getApplicationFee( required string id ) { - return apiCall( "application_fees/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function listApplicationFees( string charge, any created, string ending_before, numeric limit, string starting_after ) { - return apiCall( "application_fees", setupParams( arguments ) ); - } - - // Application Fee Refunds - - public struct function createApplicationFeeRefund( required string application_fee_id, numeric amount, struct metadata = { } ) { - return apiCall( "application_fees/#trim( arguments.application_fee_id )#/refunds", setupParams( arguments ) ); - } - - public struct function getApplicationFeeRefund( required string application_fee_id, required string id ) { - return apiCall( "application_fees/#trim( arguments.transfer_id )#/refunds/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function updateApplicationFeeRefund( required string application_fee_id, required string id, struct metadata ) { - return apiCall( "application_fees/#trim( arguments.application_fee_id )#/refunds/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function listApplicationFeeRefunds( required string application_fee_id, string ending_before, numeric limit, string starting_after ) { - return apiCall( "application_fees/#trim( arguments.application_fee_id )#/refunds", setupParams( arguments ) ); - } - - // Accounts - - public struct function createAccount( any bank_account, string business_name, string business_url, string country, boolean debit_negative_balances, string default_currency, string email, struct legal_entity, boolean managed, struct metadata, string product_description, string statement_descriptor, string support_phone, struct tos_acceptance, struct transfer_schedule ) { - return apiCall( "accounts", setupParams( arguments ), "post" ); - } - - public struct function getAccount( string id = '' ) { - return apiCall( "accounts/#trim( arguments.id )#" ); - } - - public struct function updateAccount( required string id, any bank_account, string business_name, string business_url, boolean debit_negative_balances, string default_currency, string email, struct legal_entity, struct metadata, string product_description, string statement_descriptor, string support_phone, struct tos_acceptance, struct transfer_schedule ) { - return apiCall( "accounts/#trim( arguments.id )#", setupParams( arguments ), "post" ); - } - - public struct function listAccounts( string ending_before, numeric limit, string starting_after ) { - return apiCall( "accounts", setupParams( arguments ) ); - } - - // Balance - - public struct function getBalance() { - return apiCall( "balance", setupParams( arguments ) ); - } - - public struct function getBalanceTransaction( required string id ) { - return apiCall( "balance/history/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function listBalanceHistory( any available_on, any created, string currency, string ending_before, numeric limit, string source, string starting_after, string transfer, string type ) { - return apiCall( "balance/history", setupParams( arguments ) ); - } - - // Events - - public struct function getEvent( required string id ) { - return apiCall( "events/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function listEvents( any created, string ending_before, numeric limit, string starting_after, string type ) { - return apiCall( "events", setupParams( arguments ) ); - } - - // Tokens - - public struct function createCardToken( any card, string customer ) { - return apiCall( "tokens", setupParams( arguments ), "post" ); - } - - public struct function createBankAccountToken( struct bank_account ) { - return apiCall( "tokens", setupParams( arguments ), "post" ); - } - - public struct function getToken( required string id ) { - return apiCall( "tokens/#trim( arguments.id )#", setupParams( arguments ) ); - } - - // Bitcoin - - public struct function createBitcoinReceiver( required numeric amount, string currency = variables.defaultCurrency, string email, string description, struct metadata, boolean refund_mispayments ) { - return apiCall( "bitcoin/receivers", setupParams( arguments ), "post" ); - } - - public struct function getBitcoinReceiver( required string id ) { - return apiCall( "bitcoin/receivers/#trim( arguments.id )#", setupParams( arguments ) ); - } - - public struct function listBitcoinReceivers( boolean active, string ending_before, boolean filled, numeric limit, string starting_after, boolean uncaptured_funds ) { - return apiCall( "bitcoin/receivers", setupParams( arguments ) ); - } - - // File Uploads - - public struct function createFileUpload( required string file, required string purpose ) { - return apiCall( "files", setupParams( arguments ), "post", true ); - } - - public struct function getFileUpload( required string id ) { - return apiCall( "files/#trim( arguments.id )#", setupParams( arguments ), "get", true ); - } - - public struct function listFileUploads( any created, string ending_before, numeric limit, string purpose, string starting_after ) { - return apiCall( "files", setupParams( arguments ), "get", true ); - } - - // PRIVATE FUNCTIONS - - private struct function apiCall( required string path, array params = [ ], string method = "get", boolean fileUpload = false ) { - var fullApiPath = ( fileUpload ? variables.fileUploadBaseUrl : variables.apiBaseUrl ) & arguments.path; - var requestStart = getTickCount(); - var apiResponse = makeHttpRequest( urlPath = fullApiPath, params = arguments.params, method = arguments.method ); - var result = { "api_request_time" = getTickCount() - requestStart, "status_code" = listFirst( apiResponse.statuscode, " " ), "status_text" = listRest( apiResponse.statuscode, " " ) }; - if ( variables.includeRaw ) { - result[ "raw" ] = { "method" = ucase( arguments.method ), "path" = fullApiPath, "params" = serializeJSON( arguments.params ), "response" = apiResponse.fileContent }; - } - structAppend( result, deserializeJSON( apiResponse.fileContent ), true ); - if ( variables.convertUTCTimestamps || variables.convertToCents ) parseResult( result ); - return result; - } - - private any function makeHttpRequest( required string urlPath, required array params, required string method ) { - var http = new http( url = arguments.urlPath, method = arguments.method, username = variables.apiKey, password = "" ); - - // adding a user agent header so that Adobe ColdFusion doesn't get mad about empty HTTP posts - http.addParam( type = "header", name = "User-Agent", value = "stripe.cfc" ); - - var qs = [ ]; - - // look for special params 'idempotencyKey','stripeAccount','apiKey' - for ( var param in arguments.params ) { - - if ( param.name == "idempotencyKey" ) { - http.addParam( type = "header", name = "Idempotency-Key", value = param.value ); - } else if ( param.name == "apiKey" ) { - http.setUsername( param.value ); - } else if ( param.name == "stripeAccount" ) { - http.addParam( type = "header", name = "Stripe-Account", value = param.value ); - } else if ( arguments.method == "post" ) { - if ( param.name == "file" ) { - http.addParam( type = "file", name = lcase( param.name ), file = param.value ); - } else { - http.addParam( type = "formfield", name = lcase( param.name ), value = param.value ); - } - } else if ( arrayFind( [ "get","delete" ], arguments.method ) ) { - arrayAppend( qs, lcase( param.name ) & "=" & encodeurl( param.value ) ); - } - - } - - if ( arrayLen( qs ) ) { - http.setUrl( arguments.urlPath & "?" & arrayToList( qs, "&" ) ); - } - - return http.send().getPrefix(); - } - - private array function setupParams( required struct params, boolean includeIds = false ) { - var filteredParams = { }; - var paramKeys = structKeyArray( arguments.params ); - for ( var paramKey in paramKeys ) { - if ( structKeyExists( arguments.params, paramKey ) && !isNull( arguments.params[ paramKey ] ) && ( ( paramKey != "id" && right( paramKey, 3 ) != "_id" ) || arguments.includeIds ) ) { - filteredParams[ paramKey ] = params[ paramKey ]; - } - } - return parseDictionary( filteredParams ); - } - - private array function parseDictionary( required struct dictionary, string name = '', string root = '' ) { - var result = [ ]; - var structFieldExists = structKeyExists( variables.dictionaryFields, arguments.name ); - - // validate required dictionary keys based on variables.dictionaries - if ( structFieldExists ) { - for ( var field in variables.dictionaryFields[ arguments.name ].required ) { - if ( !structKeyExists( arguments.dictionary, field ) ) { - throwError( "'#arguments.name#' dictionary missing required field: #field#" ); - } - } - } - - // special metadata handling -- metadata has 20 key limit and is cleared by passing empty metadata struct - if ( arguments.name == "metadata" ) { - if ( arrayLen( structKeyArray( arguments.dictionary ) ) > 20 ) { - throwError( "There can be a maximum of 20 keys in a metadata struct." ); - } - if ( structIsEmpty( arguments.dictionary ) ) { - arrayAppend( result, { name = arguments.root, value = "" } ); - } - } - - for ( var key in arguments.dictionary ) { - - // confirm that key is a valid one based on variables.dictionaries - if ( structFieldExists && arguments.name != "metadata" && !( arrayFindNoCase( variables.dictionaryFields[ arguments.name ].required, key ) || arrayFindNoCase( variables.dictionaryFields[ arguments.name ].optional, key ) ) ) { - throwError( "'#arguments.name#' dictionary has invalid field: #key#" ); - } - - var fullKey = len( arguments.root ) ? arguments.root & '[' & lcase( key ) & ']' : lcase( key ); - if ( isStruct( arguments.dictionary[ key ] ) ) { - for ( var item in parseDictionary( arguments.dictionary[ key ], key, fullKey ) ) { - arrayAppend( result, item ); - } - } else if ( isArray( arguments.dictionary[ key ] ) ) { - for ( var item in parseArray( arguments.dictionary[ key ], key, fullKey ) ) { - arrayAppend( result, item ); - } - } else { - // note: metadata struct is special - no validation is done on it - arrayAppend( result, { name = fullKey, value = getValidatedParam( key, arguments.dictionary[ key ], arguments.name != "metadata" ) } ); - } - - } - - return result; - } - - private array function parseArray( required array list, string name = '', string root = '' ) { - var result = [ ]; - var index = 0; - var arrayFieldExists = arrayFindNoCase( variables.arrayFields, arguments.name ); - - if ( !arrayFieldExists ) { - throwError( "'#arguments.name#' is not an allowed list variable." ); - } - - for ( var item in arguments.list ) { - if ( isStruct( item ) ) { - var fullKey = len( arguments.root ) ? arguments.root & "[" & index & "]" : arguments.name & "[" & index & "]"; - for ( var item in parseDictionary( item, '', fullKey ) ) { - arrayAppend( result, item ); - } - ++index; - } else { - var fullKey = len( arguments.root ) ? arguments.root & "[]" : arguments.name & "[]"; - arrayAppend( result, { name = fullKey, value = getValidatedParam( arguments.name, item ) } ); - } - } - - return result; - } - - private any function getValidatedParam( required string paramName, required any paramValue, boolean validate = true ) { - // only simple values - if ( !isSimpleValue( paramValue ) ) throwError( "'#paramName#' is not a simple value." ); - - // if not validation just result trimmed value - if ( !arguments.validate ) { - return trim( paramValue ); - } - - // integer - if ( arrayFindNoCase( variables.integerFields, paramName ) ) { - if ( !isInteger( paramValue ) ) { - throwError( "field '#paramName#' requires an integer value" ); - } - return paramValue; - } - // numeric - if ( arrayFindNoCase( variables.numericFields, paramName ) ) { - if ( !isNumeric( paramValue ) ) { - throwError( "field '#paramName#' requires a numeric value" ); - } - return paramValue; - } - - // currency - if ( arrayFindNoCase( variables.currencyFields, paramName ) ) { - if ( !( isNumeric( paramValue ) && ( variables.convertToCents || isValid( "integer", paramValue ) ) ) ) { - throwError( "field '#paramName#' requires an numeric/integer value" ); - } - // added rounding due to possible inaccuracies in multiplication - see https://issues.jboss.org/browse/RAILO-767 - return ( variables.convertToCents ? round( paramValue * 100 ) : paramValue ); - } - - // boolean - if ( arrayFindNoCase( variables.booleanFields, paramName ) ) { - return ( paramValue ? "true" : "false" ); - } - - // timestamp - if ( arrayFindNoCase( variables.timestampFields, paramName ) ) { - return parseUTCTimestampField( paramValue, paramName ); - } - - // default is string - return trim( paramValue ); - } - - private any function parseUTCTimestampField( required any utcField, required string utcFieldName ) { - if ( isInteger( arguments.utcField ) ) return arguments.utcField; - if ( isDate( arguments.utcField ) ) return getUTCTimestamp( arguments.utcField ); - if ( arguments.utcField == 'now' && arguments.utcFieldName == 'trial_end' ) return 'now'; - throwError( "utc timestamp field '#utcFieldName#' is in an invalid format" ); - } - - private void function parseResult( required struct result ) { - var resultKeys = structKeyArray( result ); - for ( var key in resultKeys ) { - if ( structKeyExists( result, key ) && !isNull( result[ key ] ) ) { - if ( isStruct( result[ key ] ) ) parseResult( result[ key ] ); - if ( isArray( result[ key ] ) ) { - for ( var item in result[ key ] ) { - if ( isStruct( item ) ) parseResult( item ); - } - } - if ( variables.convertUTCTimestamps && arrayFindNoCase( variables.timestampFields, key ) ) result[ key ] = parseUTCTimestamp( result[ key ] ); - if ( variables.convertToCents && arrayFindNoCase( variables.currencyFields, key ) && isInteger( result[ key ] ) ) result[ key ] = result[ key ] / 100; - } - } - } - - private string function encodeurl( required string str ) { - return replacelist( urlEncodedFormat( arguments.str, "utf-8" ), "%2D,%2E,%5F,%7E", "-,.,_,~" ); - } - - private numeric function getUTCTimestamp( required date dateToConvert ) { - return dateDiff( "s", variables.utcBaseDate, dateToConvert ); - } - - private date function parseUTCTimestamp( required numeric utcTimestamp ) { - var parsed_date = dateAdd( "s", arguments.utcTimestamp, variables.utcBaseDate ); - return parsed_date; - } - - private boolean function isInteger( required any varToValidate ) { - return ( isNumeric( arguments.varToValidate ) && isValid( "integer", arguments.varToValidate ) ); - } - - private void function throwError( required string errorMessage ) { - throw( type = "Stripe", message = "(stripe.cfc) " & arguments.errorMessage ); - } - -} +component { + + public struct function init( string apiKey = '', struct config = { } ) { + var configObj = new lib.config( apiKey, config ); + var basePath = getDirectoryFromPath( getMetadata( this ).path ).replace( '\', '/', 'all' ); + variables.objectMetadata = loadMetadata( basePath ); + variables.httpService = new lib.httpService(); + variables.parsers = { + arguments: new lib.parsers.arguments( configObj ), + headers: new lib.parsers.headers( configObj ), + response: new lib.parsers.response( configObj, objectMetadata ) + }; + + for ( var resourceFile in listResources( basePath ) ) { + var resource = resourceFile.listFirst( '.' ); + this[ resource ] = new 'lib.resources.#resource#'( this, configObj ); + } + + this[ 'webhooks' ] = new lib.webhooks( parsers.response ); + + return this; + } + + public any function call( + required string resourceName, + required string methodName, + required any argCollection, + required struct methodMetadata, + struct argOverrides = { } + ) { + var argumentsType = determineArgumentsType( argCollection, methodMetadata ); + var sources = getSources( argCollection, methodMetadata, argumentsType ); + var ignoredArgs = [ ]; + + // collect positional args and add to params + if ( argumentsType == 'positional' ) { + var argIndex = 1; + for ( var argName in methodMetadata.positionalArgs ) { + if ( arrayLen( argCollection ) < argIndex ) { + throw( + '`#resourceName#.#methodName#()` missing positional argument `#argName#` at index [#argIndex#]' + ); + } else if ( !isSimpleValue( argCollection[ argIndex ] ) ) { + throw( + '`#resourceName#.#methodName#()` positional argument `#argName#` at index [#argIndex#] is not a simple value' + ); + } else { + sources.params[ argName ] = argCollection[ argIndex ]; + } + + argIndex++; + } + } else if ( argumentsType == 'nested' ) { + for ( var argName in methodMetadata.positionalArgs ) { + if ( !structKeyExists( argCollection, argName ) ) { + throw( '`#resourceName#.#methodName#()` missing required argument `#argName#`' ); + } + sources.params[ argName ] = argCollection[ argName ]; + } + } + + sources.params.append( argOverrides, true ); + + // path + var path = 'https://' & methodMetadata.endpoint & methodMetadata.path; + for ( var argName in methodMetadata.pathArgs ) { + path = path.replace( '{#argName#}', sources.params[ argName ] ); + ignoredArgs.append( argName ); + } + + // headers + var headerData = parsers.headers.parse( sources.headers, methodMetadata ); + ignoredArgs.append( headerData.headerArgNames, true ); + + // params + var params = parsers.arguments.parse( sources.params, methodMetadata.arguments, ignoredArgs ); + + var requestStart = getTickCount(); + var rawResponse = httpService.makeRequest( + methodMetadata.httpmethod, + path, + headerData.headers, + params, + methodMetadata.multipart + ); + + var response = { }; + response[ 'duration' ] = getTickCount() - requestStart; + response[ 'requestId' ] = rawResponse.responseheader[ 'Request-Id' ]; + response[ 'headers' ] = rawResponse.responseheader; + response[ 'status' ] = listFirst( rawResponse.statuscode, ' ' ); + response[ 'content' ] = deserializeJSON( rawResponse.filecontent ); + + parsers.response.parse( response.content ); + + return response; + } + + public string function determineArgumentsType( required any argCollection, required struct methodMetadata ) { + if ( arrayLen( argCollection ) == 0 || structKeyExists( argCollection, '1' ) ) { + return 'positional'; + } + var nestedNamedKeys = [ 'params', 'headers' ]; + for ( var key in structKeyArray( argCollection ) ) { + if ( !( methodMetadata.positionalArgs.findNoCase( key ) || nestedNamedKeys.findNoCase( key ) ) ) { + return 'flat'; + } + } + return 'nested'; + } + + private any function getSources( + required any argCollection, + required struct methodMetadata, + required string argumentsType + + ) { + var sourceKeys = [ { name: 'params', offset: 1 }, { name: 'headers', offset: 2 } ]; + var sources = { }; + for ( var source in sourceKeys ) { + if ( argumentsType == 'positional' ) { + var sourceIndex = arrayLen( methodMetadata.positionalArgs ) + source.offset; + sources[ source.name ] = arrayLen( argCollection ) >= sourceIndex ? argCollection[ sourceIndex ] : { }; + } else if ( argumentsType == 'nested' ) { + sources[ source.name ] = structKeyExists( argCollection, source.name ) ? argCollection[ source.name ] : { }; + } else { + sources[ source.name ] = argCollection; + } + } + return sources; + } + + private struct function loadMetadata( required string basePath ) { + var metadata = { }; + var jsonFiles = directoryList( '#basePath#/metadata/', false, 'path' ); + for ( var path in jsonFiles ) { + var metaName = path.replace( '\', '/', 'all' ) + .listLast( '/' ) + .listFirst( '.' ); + metadata[ metaName ] = deserializeJSON( fileRead( path ) ); + } + return metadata; + } + + private array function listResources( required string basePath ) { + return directoryList( '#basePath#/lib/resources', false, 'name', '*.cfc' ); + } + +} diff --git a/tests/Application.cfc b/tests/Application.cfc index 376fceb..2daca32 100644 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -1,11 +1,11 @@ -component { - - this.mappings[ '/stripe' ] = getDirectoryFromPath( getCurrentTemplatePath() ) & '../'; - - public void function onRequestStart() { - // test stripe secret key - param name="form.stripeTESTSecretKey" default=""; - request.apiKey = form.stripeTESTSecretKey; - } - -} \ No newline at end of file +component { + rootPath = getDirectoryFromPath( getCurrentTemplatePath() ).replace( '\', '/', 'all' ).replaceNoCase( 'tests/', '' ); + + this.mappings[ "/tests" ] = rootPath & '/tests'; + this.mappings[ "/lib" ] = rootPath & '/lib'; + + public boolean function onRequestStart( String targetPage ) { + setting requestTimeout="9999"; + return true; + } +} diff --git a/tests/accountTests.cfc b/tests/accountTests.cfc deleted file mode 100644 index a5ab8ef..0000000 --- a/tests/accountTests.cfc +++ /dev/null @@ -1,45 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey, includeRaw = true ); - - } - - public void function testCreateAccount() { - - var result = stripe.createAccount( email = 'test-#createUUID()#@example.com' ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testGetAccountDetails() { - - var result = stripe.getAccountDetails(); - var account = stripe.createAccount( email = 'test-#createUUID()#@example.com' ); - var resultTwo = stripe.getAccount( account.id ); - - debug( result ); - debug( account ); - debug( resultTwo ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( 200, account.status_code, "expected a 200 status" ); - assertEquals( 200, resultTwo.status_code, "expected a 200 status" ); - - } - - public void function testListAccounts() { - - var result = stripe.listAccounts(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - -} \ No newline at end of file diff --git a/tests/bitcoinTests.cfc b/tests/bitcoinTests.cfc deleted file mode 100644 index 8c3718b..0000000 --- a/tests/bitcoinTests.cfc +++ /dev/null @@ -1,55 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey ); - - } - - public void function testCreateBitcoinReceiver() { - - var result = stripe.createBitcoinReceiver( amount = 2000, email = 'test_' & createUUID() & '@example.com' ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful request" ); - - } - - public void function testgetBitcoinReceiver() { - - var receiver = stripe.createBitcoinReceiver( amount = 2000, email = 'test_' & createUUID() & '@example.com' ); - var result = stripe.getBitcoinReceiver( receiver.id ); - - debug( receiver ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful request" ); - - } - - public void function testListBitcoinReceivers() { - - var result = stripe.listBitcoinReceivers(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful request" ); - - } - - public void function testCreateChargeWithBitcoin() { - - var receiver = stripe.createBitcoinReceiver( amount = 2000, email = 'test_' & createUUID() & '@example.com' ); - sleep( 3000 ); - var result = stripe.createCharge( amount = 2000, source = receiver.id ); - - - debug( receiver ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful request" ); - - } - -} \ No newline at end of file diff --git a/tests/chargeTests.cfc b/tests/chargeTests.cfc deleted file mode 100644 index 4bfd19b..0000000 --- a/tests/chargeTests.cfc +++ /dev/null @@ -1,271 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey, includeRaw = true ); - - } - - public void function testCreateChargeWithCard() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var result = stripe.createCharge( amount = 1999, source = source ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful charge" ); - assertTrue( result.captured, "charge was not captured" ); - - } - - public void function testCreateChargeWithToken() { - - var card = { number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var token = stripe.createCardToken( card ); - var result = stripe.createCharge( amount = 2000, source = token.id ); - - debug( token ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful charge" ); - assertTrue( result.captured, "charge was not captured" ); - - } - - public void function testCreateChargeWithCustomer() { - - var source = { object = 'card', number = '5555555555554444', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customer = stripe.createCustomer( email = 'johndoe@example.com', description = 'customer description', source = source ); - var result = stripe.createCharge( amount = 2000, customer = customer.id ); - - debug( customer ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful charge" ); - assertTrue( result.captured, "charge was not captured" ); - assertEquals( 2000, result.amount, "wrong amount charged" ); - - } - - - public void function testCreateChargeWithCustomerAndCardID() { - - var source = { object = 'card', number = '5555555555554444', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customer = stripe.createCustomer( email = 'johndoe@example.com', description = 'customer expand', source = source ); - var result = stripe.createCharge( amount = 2000, customer = customer.id, source = customer.default_source, expand = ['customer','invoice'] ); - - debug( customer ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful charge" ); - assertTrue( result.captured, "charge was not captured" ); - assertEquals( 2000, result.amount, "wrong amount charged" ); - - } - - public void function testCreateChargeWithCardAndMetadata() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var metadata = { key = 'test', foo = 'bar', id = 200 }; - var result = stripe.createCharge( amount = 1999, source = source, metadata = metadata ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful charge" ); - assertEquals( metadata, result.metadata, "metadata does not match" ); - assertTrue( result.captured, "charge was not captured" ); - - } - - public void function testCreateChargeWithApplicationFeeAndConvertToCents() { - - var stripe_convertToCents = new stripe.stripe( apiKey = request.apiKey, convertToCents = true ); - var newAccount = stripe.createAccount( email = 'test-#createUUID()#@example.com' ); - var card = { number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var token = stripe_convertToCents.createCardToken( card ); - var result = stripe_convertToCents.createCharge( amount = 19.99, source = token.id, stripeAccount = newAccount.id, application_fee = 5 ); - - debug( newAccount ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - // test only passes if stripe.com is set to decline charges with failed zip checks - public void function testFailedZipCreateChargeWithCard() { - - var source = { object = 'card', number = '4000000000000010', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ), address_zip = '12345' }; - var result = stripe.createCharge( amount = 2000, source = source ); - - debug( result ); - - assertEquals( 402, result.status_code, "expected a 402 status" ); - assertTrue( structKeyExists( result, 'error' ) && isStruct( result.error ), "missing error struct" ); - assertEquals( result.error.code, 'incorrect_zip', "wrong error code" ); - } - - // test only passes if stripe.com is set to decline charges with failed cvc checks - public void function testFailedCvcCreateChargeWithCard() { - - var source = { object = 'card', number = '4000000000000101', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ), cvc = '123' }; - var result = stripe.createCharge( amount = 2000, source = source ); - - debug( result ); - - assertEquals( 402, result.status_code, "expected a 402 status" ); - assertTrue( structKeyExists( result, 'error' ) && isStruct( result.error ), "missing error struct" ); - assertEquals( result.error.code, 'incorrect_cvc', "wrong error code" ); - } - - public void function testCardDeclinedCreateChargeWithCard() { - - var source = { object = 'card', number = '4000000000000002', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ), cvc = '123' }; - var result = stripe.createCharge( amount = 2000, source = source ); - var charge = stripe.getCharge( result.error.charge ); - - debug( result ); - debug( charge ); - - assertEquals( 402, result.status_code, "expected a 402 status" ); - assertTrue( structKeyExists( result, 'error' ) && isStruct( result.error ), "missing error struct" ); - assertEquals( result.error.code, 'card_declined', "wrong error code" ); - } - - public void function testChargeRetrieval() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 2000, source = source ); - var result = stripe.getCharge( charge.id ); - - debug( charge ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( charge.id, result.id, "wrong charge retrieved" ); - assertEquals( right( card.number, 4 ), result.source.last4, "wrong card retrieved" ); - - } - - public void function testChargeCapture() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source, capture = false ); - var result = stripe.captureCharge( charge.id, 2500 ); - - debug( charge ); - debug( result ); - - assertFalse( charge.captured, "original charge not supposed to be captured" ); - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( result.amount_refunded, 500, "wrong amount charged" ); - assertTrue( result.captured, "charge supposed to be captured" ); - - } - - public void function testListChargesWithCreatedDate() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var result = stripe.listCharges( created = charge.created ); - - debug( charge ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testListChargesWithCreatedTimestamp() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var result = stripe.listCharges( created = int( charge.created.getTime() / 1000 ) ); - - debug( charge ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testListChargesWithCreatedDictionary() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var chargeOne = stripe.createCharge( amount = 3000, source = source ); - var chargeTwo = stripe.createCharge( amount = 2500, source = source ); - var created = { "gte" = chargeOne.created, "lt" = dateAdd( "d", 1, chargeOne.created ) }; - var result = stripe.listCharges( created = created, limit = 2 ); - - debug( chargeOne ); - debug( chargeTwo ); - debug( created ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testListChargesWithPagination() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var chargeOne = stripe.createCharge( amount = 3000, source = source ); - var chargeTwo = stripe.createCharge( amount = 2500, source = source ); - var result = stripe.listCharges( limit = 1, starting_after = chargeTwo.id, include = [ 'total_count' ] ); - - debug( chargeOne ); - debug( chargeTwo ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( chargeOne.id, result.data[ 1 ].id, "wrong charge returned" ); - assertTrue( structKeyExists( result, "total_count" ), "total_count missing" ); - - } - - public void function testUpdateChargeDispute() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var result = stripe.updateChargeDispute( id = charge.id, evidence = { cancellation_policy_disclosure = "Here's evidence showing this charge is legitimate." } ); - - debug( charge ); - debug( result ); - - assertEquals( 404, result.status_code, "expected a 404 invalid_request_error" ); - - } - - public void function testCloseChargeDispute() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var result = stripe.closeChargeDispute( charge.id ); - - debug( charge ); - debug( result ); - - assertEquals( 404, result.status_code, "expected a 404 invalid_request_error" ); - - } - - public void function testUpdateCharge() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var metadata = { key = 'test', foo = 'bar', id = 200 }; - metadata = { }; - var chargeResult = stripe.createCharge( amount = 1999, source = source, description = 'description', metadata = metadata ); - var result = stripe.updateCharge( id = chargeResult.id, description = 'new description', metadata = {} ); - - debug( chargeResult ); - debug( result ); - - assertEquals( 200, chargeResult.status_code, "expected a successful charge" ); - assertEquals( 200, result.status_code, "expected a successful charge update" ); - assertEquals( 'new description', result.description, "expected description to be updated" ); - assertTrue( structIsEmpty( result.metadata ), "expected metadata to be cleared" ); - - } - - -} \ No newline at end of file diff --git a/tests/couponTests.cfc b/tests/couponTests.cfc deleted file mode 100644 index 7e4d953..0000000 --- a/tests/couponTests.cfc +++ /dev/null @@ -1,105 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testCreateCouponAmountOff() { - - var coupon = { amount_off = 500, duration = 'once', max_redemptions = 1, redeem_by = dateAdd( "d", 1, now() ) }; - - var result = stripe.createCoupon( argumentCollection = coupon ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "coupon", result.object, "coupon object was not returned" ); - - } - - public void function testCreateCouponPercentOff() { - - var coupon = { percent_off = 50, duration = 'repeating', duration_in_months = 5 }; - - var result = stripe.createCoupon( argumentCollection = coupon ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "coupon", result.object, "coupon object was not returned" ); - - } - - public void function testGetCoupon() { - - var coupon = { id = createUUID(), percent_off = 50, duration = 'repeating', duration_in_months = 5 }; - - var couponObject = stripe.createCoupon( argumentCollection = coupon ); - var result = stripe.getCoupon( couponObject.id ); - - debug( couponObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( coupon.id, result.id, "correct coupon object was not returned" ); - assertEquals( coupon.duration, result.duration, "correct coupon object was not returned" ); - } - - public void function testUpdateCoupon() { - - var coupon = { id = createUUID(), percent_off = 50, duration = 'repeating', duration_in_months = 5 }; - - var couponObject = stripe.createCoupon( argumentCollection = coupon ); - var result = stripe.updateCoupon( id = couponObject.id, metadata = { foo = 'test' } ); - - debug( couponObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( { foo = 'test' }, result.metadata, "metadata was not updated" ); - } - - public void function testDeleteCoupon() { - - var coupon = { id = createUUID(), percent_off = 50, duration = 'repeating', duration_in_months = 5 }; - - var couponObject = stripe.createCoupon( argumentCollection = coupon ); - var result = stripe.deleteCoupon( coupon.id ); - - debug( couponObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( coupon.id, result.id, "correct coupon object was not returned" ); - assertTrue( result.deleted, "coupon was not deleted" ); - - } - - public void function testListCoupons() { - - var couponOne = { id = createUUID(), amount_off = 500, duration = 'once', max_redemptions = 1, redeem_by = dateAdd( "d", 1, now() ) }; - var couponTwo = { id = createUUID(), percent_off = 50, duration = 'repeating', duration_in_months = 5 }; - - var couponOneObject = stripe.createCoupon( argumentCollection = couponOne ); - var couponTwoObject = stripe.createCoupon( argumentCollection = couponTwo ); - var result = stripe.listCoupons( limit = 2 ); - - debug( couponOneObject ); - debug( couponTwoObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( arrayLen( result.data ) == 2, "coupons are not listed" ); - - } - - private date function parseUTCTimestamp( required numeric utcTimestamp ) { - var utcdate = dateAdd( "s", arguments.utcTimestamp, createDate( 1970,1,1 ) ); - return dateAdd("s", getTimeZoneInfo().utcTotalOffset * -1, utcdate ); - } - - - -} \ No newline at end of file diff --git a/tests/customerTests.cfc b/tests/customerTests.cfc deleted file mode 100644 index a2eba76..0000000 --- a/tests/customerTests.cfc +++ /dev/null @@ -1,171 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testCreateCustomer() { - - var result = stripe.createCustomer(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "customer", result.object, "customer object was not returned" ); - - } - - public void function testCreateCustomerWithEmailAndDescription() { - - var result = stripe.createCustomer( email = "test@example.com", description = "this is a test description" ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "customer", result.object, "customer object was not returned" ); - assertEquals( "test@example.com", result.email, "correct customer object was not returned" ); - assertEquals( "this is a test description", result.description, "correct customer object was not returned" ); - - } - - public void function testCreateCustomerWithMetadata() { - - var result = stripe.createCustomer( metadata = { email = "test@example.com", description = "this is a test description" } ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "customer", result.object, "customer object was not returned" ); - assertEquals( "test@example.com", result.metadata.email, "correct metadata was not returned" ); - assertEquals( "this is a test description", result.metadata.description, "correct metadata was not returned" ); - - } - - public void function testCreateCustomerWithCard() { - - var source = { object = "card", number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var result = stripe.createCustomer( source = source ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "customer", result.object, "customer object was not returned" ); - assertTrue( structKeyExists( result, "default_source" ), "created card missing" ); - assertEquals( right( source.number, 4 ), result.sources.data[1].last4, "wrong card number in response" ); - - } - - public void function testCreateCustomerWithToken() { - - var card = { number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var token = stripe.createCardToken( card ); - var result = stripe.createCustomer( card = token.id ); - - debug( token ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "customer", result.object, "customer object was not returned" ); - assertTrue( structKeyExists( result, "default_source" ), "created card missing" ); - assertEquals( right( card.number, 4 ), result.sources.data[1].last4, "wrong card number in response" ); - - } - - public void function testGetCustomer() { - - var customer = stripe.createCustomer( description = 'Test Customer' ); - var result = stripe.getCustomer( customer.id ); - - debug( customer ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( customer.id, result.id, "correct customer object was not returned" ); - - } - - public void function testUpdateCustomer() { - - var cardOne = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var cardTwo = { object = 'card', number = '5105105105105100', exp_month = '10', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customer = stripe.createCustomer( email = 'testOne@example.com', source = cardOne ); - var result = stripe.updateCustomer( id = customer.id, email = "testTwo@example.com", source = cardTwo ); - - debug( customer ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( customer.id, result.id, "correct customer object was not returned" ); - assertEquals( "testOne@example.com", customer.email, "correct original customer object was not returned" ); - assertEquals( right( cardOne.number, 4 ), customer.sources.data[1].last4, "correct original customer card was not returned" ); - assertEquals( "testTwo@example.com", result.email, "correct new customer object was not returned" ); - assertEquals( right( cardTwo.number, 4 ), result.sources.data[1].last4, "correct new customer card was not returned" ); - - } - - public void function testUpdateCustomerDefaultSource() { - - var cardOne = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var cardTwo = { object = 'card', number = '5105105105105100', exp_month = '10', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var cardThree = { object = 'card', number = '378282246310005', exp_month = '10', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customerObject = stripe.createCustomer( source = cardOne ); - var cardCreate = stripe.createCustomerSource( customerObject.id, cardTwo ); - var result = stripe.updateCustomer( id = customerObject.id, default_source = cardCreate.id ); - var resultTwo = stripe.updateCustomer( id = customerObject.id, source = cardThree ); - - debug( customerObject ); - debug( cardCreate ); - debug( result ); - debug( resultTwo ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( cardCreate.id, result.default_source, "default_source was not updated" ); - assertFalse( result.default_source == resultTwo.default_source, "default_source was not updated" ); - - } - - public void function testUpdateCustomerMetadata() { - - var customer = stripe.createCustomer( metadata = { email = "test@example.com", description = "this is a test description" } ); - var result = stripe.updateCustomer( id = customer.id, metadata = { email = "foo@example.com", description = "" } ); - - debug( customer ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "customer", result.object, "customer object was not returned" ); - assertEquals( "foo@example.com", result.metadata.email, "correct metadata was not returned" ); - assertFalse( structKeyExists( result.metadata, "description" ), "correct metadata was not returned" ); - - } - - public void function testDeleteCustomer() { - - var card = { number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customer = stripe.createCustomer( email = 'test@example.com', card = card ); - var result = stripe.deleteCustomer( customer.id ); - - debug( customer ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( customer.id, result.id, "correct customer object was not returned" ); - assertTrue( structKeyExists( result, "deleted" ) && result.deleted, "customer not marked not as deleted" ); - - } - - public void function testListCustomers() { - - var customerOne = stripe.createCustomer(); - var customerTwo = stripe.createCustomer(); - var result = stripe.listCustomers(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - -} \ No newline at end of file diff --git a/tests/discountTests.cfc b/tests/discountTests.cfc deleted file mode 100644 index fb77c60..0000000 --- a/tests/discountTests.cfc +++ /dev/null @@ -1,47 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testDeleteCustomerDiscount() { - - var coupon = { percent_off = 50, duration = 'repeating', duration_in_months = 5 }; - var couponObject = stripe.createCoupon( argumentCollection = coupon ); - var customerObject = stripe.createCustomer( email = 'test@example.com', coupon = couponObject.id ); - var result = stripe.deleteCustomerDiscount( customerObject.id ); - - debug( couponObject ); - debug( customerObject ); - debug( result ); - - assertEquals( 200, customerObject.status_code, "expected a 200 status" ); - assertTrue( structKeyExists( result, "deleted" ) && result.deleted, "discount not marked not as deleted" ); - - } - - public void function testDeleteCustomerSubscriptionDiscount() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", trial_period_days = 30 }; - var coupon = { percent_off = 50, duration = 'repeating', duration_in_months = 5 }; - var couponObject = stripe.createCoupon( argumentCollection = coupon ); - var planObject = stripe.createPlan( argumentCollection = plan ); - var customerObject = stripe.createCustomer( source = source ); - var subscriptionObject = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id, coupon = couponObject.id ); - var result = stripe.deleteCustomerSubscriptionDiscount( customerObject.id, subscriptionObject.id ); - - debug( couponObject ); - debug( planObject ); - debug( customerObject ); - debug( subscriptionObject ); - debug( result ); - - assertEquals( 200, customerObject.status_code, "expected a 200 status" ); - assertTrue( structKeyExists( result, "deleted" ) && result.deleted, "discount not marked not as deleted" ); - - } - -} \ No newline at end of file diff --git a/tests/errorTests.cfc b/tests/errorTests.cfc deleted file mode 100644 index 528c80a..0000000 --- a/tests/errorTests.cfc +++ /dev/null @@ -1,56 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testIntegerFieldValidation() { - - var card = { number = '4242424242424242', exp_month = '5', exp_year = '14' }; - - try { - var result = stripe.createCharge( amount = 20.45, card = card ); - fail( 'invalid amount "20.45" supposed to throw error' ); - } - catch( any e ) { - debug( e ); - assertTrue( e.message != 'invalid amount "20.45" supposed to throw error', e.message ); - } - - } - - - public void function testListChargesWithInvalidCreatedDate() { - - var failMessage = 'invalid date supposed to throw error'; - - try { - var result = stripe.listCharges( created = "badDateString" ); - fail( failMessage ); - } - catch( any e ) { - debug( e ); - assertTrue( e.message != failMessage, e.message ); - } - - } - - public void function testCreateCoupon() { - - var failMessage = 'invalid date supposed to throw error'; - - try { - var result = stripe.listCharges( created = "badDateString" ); - fail( failMessage ); - } - catch( any e ) { - debug( e ); - assertTrue( e.message != failMessage, e.message ); - } - - } - - -} \ No newline at end of file diff --git a/tests/fileUploadTests.cfc b/tests/fileUploadTests.cfc deleted file mode 100644 index 8525619..0000000 --- a/tests/fileUploadTests.cfc +++ /dev/null @@ -1,42 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey, includeRaw = true ); - - } - - public void function testUploadFile() { - - var result = stripe.createFileUpload( file = expandPath( '/stripe/tests/test.png' ), purpose = 'identity_document' ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testGetFileUpload() { - - var fileupload = stripe.createFileUpload( file = expandPath( '/stripe/tests/test.png' ), purpose = 'identity_document' ); - var result = stripe.getFileUpload( fileupload.id ); - - debug( fileupload ); - debug( result ); - - assertEquals( 200, fileupload.status_code, "expected a 200 status" ); - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testListFileUploads() { - - var result = stripe.listFileUploads(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - -} \ No newline at end of file diff --git a/tests/index.cfm b/tests/index.cfm deleted file mode 100644 index b0eb4c8..0000000 --- a/tests/index.cfm +++ /dev/null @@ -1,54 +0,0 @@ - -param name="form.testsToRun" default=""; - -cfcBase = replace( replace( getDirectoryFromPath( getCurrentTemplatePath() ), expandPath( '/' ), '' ), '/', '.', 'all' ); -testSuite = createObject( "component", "mxunit.framework.TestSuite" ).TestSuite(); -metadata = { }; -cfcNames = directoryList( getDirectoryFromPath( getCurrentTemplatePath() ), false, 'name', '*.cfc' ); - -for ( cfcName in cfcNames ) { - if ( arrayFindNoCase( [ 'Application.cfc', 'testBase.cfc' ], cfcName ) ) continue; - thisCfc = cfcBase & replace( cfcName, '.cfc', '' ); - thisMetadata = getComponentMetaData( thisCfc ); - for ( i = 1; i <= arrayLen( thisMetadata.functions ); i++ ) { - if ( !structKeyExists( metadata, thisCfc ) ) metadata[ thisCfc ] = [ ]; - if ( !listFind( 'setup,beforeTests,afterTests', thisMetadata.functions[ i ].name ) && thisMetadata.functions[ i ].access == "public" ) { - arrayAppend( metadata[ thisCfc ], thisMetadata.functions[ i ].name ); - } - } -} - -for ( cfcName in structKeyArray( metadata ) ) { - functionsToRun = [ ]; - for ( functionName in metadata[ cfcName ] ) { - if ( listFind( form.testsToRun, cfcName & ":" & functionName ) ) { - arrayAppend( functionsToRun, functionName ); - } - } - testSuite.add( cfcName, arrayToList( functionsToRun ) ); -} - -results = testSuite.run(); - - - -

stripe.cfc tests

- -
- -
- - - -
- #cfcName# - -
-
-
-
- -
-

Results

-#results.getResultsOutput( 'html' )# -
\ No newline at end of file diff --git a/tests/invoiceItemTests.cfc b/tests/invoiceItemTests.cfc deleted file mode 100644 index a8e1886..0000000 --- a/tests/invoiceItemTests.cfc +++ /dev/null @@ -1,101 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey, includeRaw = true ); - customerObject = stripe.createCustomer( description = 'Test Customer' ); - - } - - public void function testCreateInvoiceItem() { - - var result = stripe.createInvoiceItem( customer = customerObject.id, amount = 5000, description = "You lose! $50 more!", metadata = { foo = "bar" } ); - - debug( customerObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "invoiceitem", result.object, "invoiceitem object was not returned" ); - assertEquals( { foo = "bar" }, result.metadata, "correct metadata was not returned" ); - - } - - public void function testCreateInvoiceItemNegativeAmount() { - - var result = stripe.createInvoiceItem( customer = customerObject.id, amount = -1000, description = "You win! $10 off!" ); - - debug( customerObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "invoiceitem", result.object, "invoiceitem object was not returned" ); - assertEquals( -1000, result.amount, "correct invoiceitem amount was not returned" ); - - } - - public void function testGetInvoiceItem() { - - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 5000, description = "You lose! $50 more!" ); - var result = stripe.getInvoiceItem( invoiceitemObject.id ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( invoiceitemObject.id, result.id, "correct invoiceitem object was not returned" ); - - } - - public void function testUpdateInvoiceItem() { - - var invoiceUpdate = { amount = 2000, description = "Changed the amount!", metadata = { test = "value", foo = "" } }; - - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 5000, description = "You lose! $50 more!", metadata = { foo = "bar" } ); - var result = stripe.updateInvoiceItem( id = invoiceitemObject.id, amount = invoiceUpdate.amount, description = invoiceUpdate.description, metadata = invoiceUpdate.metadata ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( invoiceitemObject.id, result.id, "correct invoiceitem object was not returned" ); - assertEquals( invoiceUpdate.amount, result.amount, "correct invoiceitem amount was not returned" ); - assertEquals( { test = "value" }, result.metadata, "correct invoiceitem metadata was not returned" ); - - } - - public void function testDeleteInvoiceItem() { - - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 3000, description = "You lose! $30 more!" ); - var result = stripe.deleteInvoiceItem( invoiceitemObject.id ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( invoiceitemObject.id, result.id, "correct invoiceitem object was not returned" ); - assertTrue( result.deleted, "invoiceitem was not deleted" ); - - } - - public void function testListInvoiceItems() { - - var invoiceitemOne = { customer = customerObject.id, amount = 3000, description = "You lose! $30 more!" }; - var invoiceitemTwo = { customer = customerObject.id, amount = -1000, description = "You win! $10 off!" }; - - var invoiceitemOneObject = stripe.createInvoiceItem( argumentCollection = invoiceItemOne ); - var invoiceitemTwoObject = stripe.createInvoiceItem( argumentCollection = invoiceItemTwo ); - var result = stripe.listInvoiceItems( limit = 2, customer = customerObject.id ); - - debug( invoiceitemOneObject ); - debug( invoiceitemTwoObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( arrayLen( result.data ) == 2, "invoiceitems are not listed" ); - - } - -} \ No newline at end of file diff --git a/tests/invoiceTests.cfc b/tests/invoiceTests.cfc deleted file mode 100644 index 274fa79..0000000 --- a/tests/invoiceTests.cfc +++ /dev/null @@ -1,187 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testCreateInvoice() { - - var customerObject = stripe.createCustomer( description = 'Test Customer' ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - var result = stripe.createInvoice( customerObject.id ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "invoice", result.object, "invoice object was not returned" ); - assertEquals( 1000, result.amount_due, "correct amount_due was not returned" ); - - } - - public void function testCreateInvoiceWithSubscription() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customerObject = stripe.createCustomer( description = 'Test Customer', source = source ); - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", trial_period_days = 30 }; - var planObject = stripe.createPlan( argumentCollection = plan ); - var subscriptionObjectOne = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id, trial_end = "now" ); - var subscriptionObjectTwo = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id ); - var invoiceitemObjectOne = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee", subscription = subscriptionObjectOne.id ); - var invoiceitemObjectTwo = stripe.createInvoiceItem( customer = customerObject.id, amount = 500, description = "Test Fee", subscription = subscriptionObjectTwo.id ); - var result = stripe.createInvoice( customer = customerObject.id, subscription = subscriptionObjectTwo.id ); - - debug( invoiceitemObjectOne ); - debug( invoiceitemObjectTwo ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "invoice", result.object, "invoice object was not returned" ); - assertEquals( 500, result.amount_due, "correct amount_due was not returned" ); - assertEquals( 1, arrayLen( result.lines.data ), "only one invoice item was supposed to be returned" ); - - } - - - public void function testGetInvoice() { - - var customerObject = stripe.createCustomer( description = 'Test Customer' ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - var invoiceObject = stripe.createInvoice( customerObject.id ); - var result = stripe.getInvoice( invoiceObject.id ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( invoiceObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "invoice", result.object, "invoice object was not returned" ); - assertEquals( invoiceObject.id, result.id, "correct invoice was not returned" ); - - } - - public void function testGetInvoiceLineItems() { - - var customerObject = stripe.createCustomer( description = 'Test Customer' ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - var invoiceObject = stripe.createInvoice( customerObject.id ); - var result = stripe.getInvoiceLineItems( id = invoiceObject.id, limit = 5 ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( invoiceObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "list", result.object, "list object was not returned" ); - assertEquals( 1, arrayLen( result.data ), "correct data was not returned" ); - - } - - public void function testPayInvoice() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - - var customerObject = stripe.createCustomer( source = source ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - var invoiceObject = stripe.createInvoice( customerObject.id ); - var result = stripe.payInvoice( invoiceObject.id ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( invoiceObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "invoice", result.object, "invoice object was not returned" ); - assertTrue( result.closed, "invoice was not closed" ); - assertTrue( result.paid, "invoice was not paid" ); - - } - - public void function testUpdateInvoice() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - - var customerObject = stripe.createCustomer( source = source ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - var invoiceObject = stripe.createInvoice( customerObject.id ); - var result = stripe.updateInvoice( id = invoiceObject.id, closed = true ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( invoiceObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "invoice", result.object, "invoice object was not returned" ); - assertTrue( result.closed, "invoice was not closed" ); - assertFalse( result.paid, "invoice was not supposed to be paid" ); - - } - - public void function testListInvoices() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - - var customerObject = stripe.createCustomer( source = source ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - var invoiceObject = stripe.createInvoice( customerObject.id ); - - var result = stripe.listInvoices( limit = 1 ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( invoiceObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( arrayLen( result.data ) == 1, "invoice is not listed" ); - - } - - public void function testListInvoicesWithDateDictionary() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - - var customerObject = stripe.createCustomer( source = source ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - var invoiceObject = stripe.createInvoice( customerObject.id ); - - var date = { "gte" = invoiceObject.date, "lt" = dateAdd( "d", 1, invoiceObject.date ) }; - var result = stripe.listInvoices( limit = 1, date = date ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( invoiceObject ); - debug( date ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( arrayLen( result.data ) == 1, "invoice is not listed" ); - - } - - public void function testGetUpcomingInvoice() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - - var customerObject = stripe.createCustomer( source = source ); - var invoiceitemObject = stripe.createInvoiceItem( customer = customerObject.id, amount = 1000, description = "Extra Fee" ); - - var result = stripe.getUpcomingInvoice( customerObject.id ); - - debug( customerObject ); - debug( invoiceitemObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( result.lines.total_count >= 1, "lines not returned" ); - - } - -} \ No newline at end of file diff --git a/tests/otherTests.cfc b/tests/otherTests.cfc deleted file mode 100644 index c0e247d..0000000 --- a/tests/otherTests.cfc +++ /dev/null @@ -1,74 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey, includeRaw = true ); - - } - - public void function testGetBalance() { - - var result = stripe.getBalance(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testListBalanceHistory() { - - var result = stripe.listBalanceHistory(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testGetBalanceTransaction() { - - var card = { number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var chargeObject = stripe.createCharge( amount = 1999, card = card ); - var balanceHistory = stripe.listBalanceHistory(); - var result = stripe.getBalanceTransaction( balanceHistory.data[ 1 ].id ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - - public void function testListEvents() { - - var result = stripe.listEvents(); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testGetEvent() { - - var events = stripe.listEvents(); - var result = stripe.getEvent( events.data[ 1 ].id ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testAlternateApiKey() { - - var result = stripe.getAccountDetails( apiKey = 'foo' ); - - debug( result ); - - assertEquals( 401, result.status_code, "expected a 401 status" ); - - } - -} \ No newline at end of file diff --git a/tests/planTests.cfc b/tests/planTests.cfc deleted file mode 100644 index 0ab18a6..0000000 --- a/tests/planTests.cfc +++ /dev/null @@ -1,90 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testCreatePlan() { - - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", metadata = { test = 'value' } }; - - var result = stripe.createPlan( argumentCollection = plan ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "plan", result.object, "plan object was not returned" ); - assertEquals( "value", result.metadata.test, "metadata was incorrect" ); - - } - - public void function testGetPlan() { - - var plan = { id = createUUID(), amount = 5000, interval = "year", name = "Test Get Plan", trial_period_days = 15 }; - - var planObject = stripe.createPlan( argumentCollection = plan ); - var result = stripe.getPlan( plan.id ); - - debug( planObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( plan.id, result.id, "correct plan object was not returned" ); - assertEquals( plan.amount, result.amount, "correct plan object was not returned" ); - - } - - public void function testUpdatePlan() { - - var plan = { id = createUUID(), amount = 5000, interval = "year", name = "Test Update Plan" }; - var newPlanName = "My New Plan Name"; - - var planObject = stripe.createPlan( argumentCollection = plan ); - var result = stripe.updatePlan( id = plan.id, name = newPlanName ); - - debug( planObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( plan.id, result.id, "correct plan object was not returned" ); - assertEquals( newPlanName, result.name, "plan was not updated correctly" ); - - } - - public void function testDeletePlan() { - - var plan = { id = createUUID(), amount = 5000, interval = "year", name = "Test Update Plan" }; - - var planObject = stripe.createPlan( argumentCollection = plan ); - var result = stripe.deletePlan( plan.id ); - - debug( planObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( plan.id, result.id, "correct plan object was not returned" ); - assertTrue( result.deleted, "plan was not deleted" ); - - } - - public void function testListPlans() { - - var planOne = { id = createUUID(), amount = 500, interval = "month", name = "Test Update Plan One" }; - var planTwo = { id = createUUID(), amount = 5000, interval = "year", name = "Test Update Plan Two" }; - - var planOneObject = stripe.createPlan( argumentCollection = planOne ); - var planTwoObject = stripe.createPlan( argumentCollection = planTwo ); - var result = stripe.listPlans( ending_before = planOneObject.id ); - - debug( planOneObject ); - debug( planTwoObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( 1, arrayLen( result.data ), "correct plans are not listed" ); - - } - -} \ No newline at end of file diff --git a/tests/recipientTests.cfc b/tests/recipientTests.cfc deleted file mode 100644 index 19c563b..0000000 --- a/tests/recipientTests.cfc +++ /dev/null @@ -1,112 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testCreateRecipient() { - - var result = stripe.createRecipient( name = "John Doe", type = "individual", metadata = { foo = "bar" } ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "recipient", result.object, "recipient object was not returned" ); - assertEquals( { foo = "bar" }, result.metadata, "correct metadata was not returned" ); - - } - - public void function testCreateRecipientWithBankAccount() { - - var bank_account = { country = "US", routing_number = "110000000", account_number = "000123456789" }; - var result = stripe.createRecipient( name = "John Doe", type = "individual", bank_account = bank_account ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "recipient", result.object, "recipient object was not returned" ); - assertTrue( structKeyExists( result, "active_account" ), "created account missing" ); - assertEquals( right( bank_account.account_number, 4 ), result.active_account.last4, "wrong account number in response" ); - - } - - public void function testCreateRecipientWithToken() { - - var bank_account = { country = "US", routing_number = "110000000", account_number = "000123456789" }; - var bank_account_token = stripe.createBankAccountToken( bank_account ); - var result = stripe.createRecipient( name = "John Doe", type = "individual", bank_account = bank_account_token.id ); - - debug( bank_account_token ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "recipient", result.object, "recipient object was not returned" ); - assertTrue( structKeyExists( result, "active_account" ), "created account missing" ); - assertEquals( right( bank_account.account_number, 4 ), result.active_account.last4, "wrong account number in response" ); - - } - - public void function testGetRecipient() { - - var recipient = stripe.createRecipient( name = "John Doe", type = "individual" ); - var result = stripe.getRecipient( recipient.id ); - - debug( recipient ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( recipient.id, result.id, "correct recipient object was not returned" ); - - } - - public void function testUpdateRecipient() { - - var bank_account = { country = "US", routing_number = "110000000", account_number = "000123456789" }; - var recipientObject = stripe.createRecipient( name = "John Doe", type = "individual", email = "testOne@example.com", bank_account = bank_account , metadata = { foo = "bar" } ); - var result = stripe.updateRecipient( id = recipientObject.id, email = "testTwo@example.com", metadata = { foo = "new" } ); - - debug( recipientObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( recipientObject.id, result.id, "correct customer object was not returned" ); - assertEquals( "testOne@example.com", recipientObject.email, "correct original recipient object was not returned" ); - assertEquals( "testTwo@example.com", result.email, "correct new recipient object was not returned" ); - assertEquals( { foo = "new" }, result.metadata, "correct new metadata was not returned" ); - - } - - public void function testDeleteRecipient() { - - var bank_account = { country = "US", routing_number = "110000000", account_number = "000123456789" }; - var recipientObject = stripe.createRecipient( name = "John Doe", type = "individual", email = "testOne@example.com", bank_account = bank_account ); - var result = stripe.deleteRecipient( recipientObject.id ); - - debug( recipientObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( recipientObject.id, result.id, "correct customer object was not returned" ); - assertTrue( result.deleted, "recipient not marked not as deleted" ); - - } - - public void function testListRecipients() { - - var recipientOneObject = stripe.createRecipient( name = "John Doe", type = "individual" ); - var recipientTwoObject = stripe.createRecipient( name = "Jane Doe", type = "corporation" ); - var result = stripe.listRecipients( limit = 2, include = [ 'total_count' ] ); - - debug( recipientOneObject ); - debug( recipientTwoObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( result.total_count >= 2, "total_count is missing or invalid" ); - assertTrue( arrayLen( result.data ) == 2, "recipients are not listed" ); - - } - -} \ No newline at end of file diff --git a/tests/refundTests.cfc b/tests/refundTests.cfc deleted file mode 100644 index f6ec00b..0000000 --- a/tests/refundTests.cfc +++ /dev/null @@ -1,70 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey, includeRaw = true ); - - } - - public void function testChargeRefund() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var result = stripe.createChargeRefund( charge.id, 1000 ); - - debug( charge ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( result.amount, 1000, "wrong amount refunded" ); - - } - - public void function testRefundRetrieval() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var refund = stripe.createChargeRefund( charge.id, 1000 ); - var result = stripe.getChargeRefund( charge.id, refund.id ); - - debug( charge ); - debug( refund ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( result.amount, 1000, "wrong amount refunded" ); - - } - - public void function testRefundUpdate() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var refund = stripe.createChargeRefund( charge.id, 1000 ); - var result = stripe.updateChargeRefund( charge.id, refund.id, { foo = "test" } ); - - debug( charge ); - debug( refund ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( result.metadata.foo, "test", "expected metadata missing" ); - - } - - public void function testListRefunds() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var charge = stripe.createCharge( amount = 3000, source = source ); - var refund = stripe.createChargeRefund( charge.id, 1000 ); - var result = stripe.listChargeRefunds( charge.id ); - - debug( charge ); - debug( refund ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - -} \ No newline at end of file diff --git a/tests/runner.cfm b/tests/runner.cfm new file mode 100644 index 0000000..9a2af99 --- /dev/null +++ b/tests/runner.cfm @@ -0,0 +1,12 @@ + + + testbox = new testbox.system.Testbox(); + param name="url.reporter" default="simple"; + param name="url.directory" default="tests.specs"; + args = { reporter: url.reporter, directory: url.directory }; + if ( structKeyExists( url, 'bundles' ) ) args.bundles = url.bundles; + results = testBox.run( argumentCollection = args ); + + + +#trim( results )# diff --git a/tests/sourceTests.cfc b/tests/sourceTests.cfc deleted file mode 100644 index 3e8e8d1..0000000 --- a/tests/sourceTests.cfc +++ /dev/null @@ -1,88 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey, includeRaw = true ); - - } - - public void function testCreateCustomerSource() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customerObject = stripe.createCustomer( description = 'Test Customer' ); - var result = stripe.createCustomerSource( customerObject.id, source ); - - debug( customerObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "card", result.object, "card object was not returned" ); - - } - - public void function testGetCustomerSource() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customerObject = stripe.createCustomer( source = source ); - var result = stripe.getCustomerSource( customerObject.id, customerObject.default_source ); - - debug( customerObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "card", result.object, "card object was not returned" ); - assertEquals( customerObject.default_source, result.id, "correct card object was not returned" ); - - } - - public void function testUpdateCustomerSource() { - - var source = { object = 'card', number = '4242424242424242', exp_month = 5, exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customerObject = stripe.createCustomer( description = 'Test Customer' ); - var sourceObject = stripe.createCustomerSource( customerObject.id, source ); - var result = stripe.updateCustomerSource( customer_id = customerObject.id, id = sourceObject.id, exp_year = year( dateAdd( "yyyy", 2, now() ) ) ); - - debug( customerObject ); - debug( sourceObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "card", result.object, "card object was not returned" ); - assertEquals( year( dateAdd( "yyyy", 2, now() ) ), result.exp_year, "correct card exp_year was not returned" ); - - } - - public void function testDeleteCustomerSource() { - - var source = { object = 'card', number = '4242424242424242', exp_month = 5, exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var customerObject = stripe.createCustomer( source = source ); - var result = stripe.deleteCustomerSource( customer_id = customerObject.id, id = customerObject.default_source ); - - debug( customerObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( result.deleted, "card object was not deleted" ); - - } - - - public void function testListCustomerSources() { - - var cardOne = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var cardTwo = { object = 'card', number = '5105105105105100', exp_month = '10', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - - var customerObject = stripe.createCustomer( source = cardOne ); - var cardCreate = stripe.createCustomerSource( customerObject.id, cardTwo ); - var result = stripe.listCustomerSources( customer_id = customerObject.id, include = [ 'total_count' ], limit = 2 ); - - debug( customerObject ); - debug( cardCreate ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( arrayLen( result.data ) == 2, "cards are not listed" ); - - } - -} \ No newline at end of file diff --git a/tests/specs/apiCallArgumentsSpec.cfc b/tests/specs/apiCallArgumentsSpec.cfc new file mode 100644 index 0000000..8221fbd --- /dev/null +++ b/tests/specs/apiCallArgumentsSpec.cfc @@ -0,0 +1,72 @@ +component extends=testbox.system.BaseSpec { + + function beforeAll() { + stripe = new stripe( 'fake_key' ); + httpService = getProperty( stripe, 'httpService' ); + prepareMock( httpService ); + httpService.$( 'exec', { responseHeader: { 'Request-Id': '' }, statuscode: 200, filecontent: '{}' } ); + } + + function run() { + + describe( 'An API call', function() { + + afterEach( function() { + httpService.$reset(); + } ); + + it( 'supports named arguments', function() { + var res = stripe.charges.create( customer = 'customer_id', amount = 2000, currency = 'usd' ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.body ).toBe( 'amount=2000¤cy=usd&customer=customer_id' ); + } ); + + it( 'supports named arguments used in the URL', function() { + var res = stripe.charges.retrieve( charge_id = 'charge_id' ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.attrColl.url ).toBe( 'https://api.stripe.com/v1/charges/charge_id' ); + } ); + + it( 'supports named arguments used in the URL and body', function() { + var res = stripe.customers.updateSource( customer_id = 'customer_id', source_id = 'source_id', metadata = { 'a': 1 } ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.attrColl.url ).toBe( 'https://api.stripe.com/v1/customers/customer_id/sources/source_id' ); + expect( httpRequest.body ).toBe( 'metadata%5Ba%5D=1' ); + } ); + + it( 'supports param arguments used in the URL', function() { + var res = stripe.usageRecords.create( params = { 'subscription_item': 'subscription_item_id' } ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.attrColl.url ).toBe( 'https://api.stripe.com/v1/subscription_items/subscription_item_id/usage_records' ); + } ); + + it( 'supports named "params" argument', function() { + var res = stripe.charges.create( params = { customer: 'customer_id', amount: 2000, currency: 'usd' } ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.body ).toBe( 'amount=2000¤cy=usd&customer=customer_id' ); + } ); + + it( 'supports positional arguments', function() { + var res = stripe.charges.create( { customer: 'customer_id', amount: 2000, currency: 'usd' } ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.body ).toBe( 'amount=2000¤cy=usd&customer=customer_id' ); + } ); + + it( 'supports positional arguments used in the URL', function() { + var res = stripe.charges.retrieve( 'charge_id' ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.attrColl.url ).toBe( 'https://api.stripe.com/v1/charges/charge_id' ); + } ); + + it( 'supports positional arguments used in the URL and body', function() { + var res = stripe.customers.updateSource( 'customer_id', 'source_id', { metadata: { 'a': 1 } } ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.attrColl.url ).toBe( 'https://api.stripe.com/v1/customers/customer_id/sources/source_id' ); + expect( httpRequest.body ).toBe( 'metadata%5Ba%5D=1' ); + } ); + + } ); + + } + +} diff --git a/tests/specs/apiCallHeadersSpec.cfc b/tests/specs/apiCallHeadersSpec.cfc new file mode 100644 index 0000000..a8c161c --- /dev/null +++ b/tests/specs/apiCallHeadersSpec.cfc @@ -0,0 +1,61 @@ +component extends=testbox.system.BaseSpec { + + function beforeAll() { + stripe = new stripe( 'fake_key' ); + httpService = getProperty( stripe, 'httpService' ); + prepareMock( httpService ); + httpService.$( 'exec', { responseHeader: { 'Request-Id': '' }, statuscode: 200, filecontent: '{}' } ); + } + + function run() { + + describe( 'A Header for an API call', function() { + + afterEach( function() { + httpService.$reset(); + } ); + + it( 'can be passed as a named argument', function() { + var res = stripe.charges.create( customer = 'customer_id', amount = 2000, currency = 'usd', stripeAccount = 'account_id' ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.headers ).toHaveLength( 3 ); + expect( httpRequest.headers[ 3 ].name ).toBeWithCase( 'Stripe-Account' ); + expect( httpRequest.headers[ 3 ].value ).toBeWithCase( 'account_id' ); + } ); + + it( 'can be passed in a named argument struct', function() { + var res = stripe.charges.create( + params = { customer: 'customer_id', amount: 2000, currency: 'usd' }, + headers = { stripeAccount: 'account_id' } + ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.headers ).toHaveLength( 3 ); + expect( httpRequest.headers[ 3 ].name ).toBeWithCase( 'Stripe-Account' ); + expect( httpRequest.headers[ 3 ].value ).toBeWithCase( 'account_id' ); + } ); + + it( 'can be passed positionally in a struct', function() { + var res = stripe.charges.create( + { customer: 'customer_id', amount: 2000, currency: 'usd' }, + { stripeAccount: 'account_id' } + ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.headers ).toHaveLength( 3 ); + expect( httpRequest.headers[ 3 ].name ).toBeWithCase( 'Stripe-Account' ); + expect( httpRequest.headers[ 3 ].value ).toBeWithCase( 'account_id' ); + } ); + + it( 'overrides the corresponding config setting', function() { + var res = stripe.accounts.retrieve( apiKey = 'fake_key_2' ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.headers ).toHaveLength( 1 ); + expect( httpRequest.headers[ 1 ].name ).toBeWithCase( 'Authorization' ); + expect( httpRequest.headers[ 1 ].value ).toBeWithCase( 'Bearer fake_key_2' ); + } ); + + + } ); + + } + +} diff --git a/tests/specs/argumentParserSpec.cfc b/tests/specs/argumentParserSpec.cfc new file mode 100644 index 0000000..6e3c3ae --- /dev/null +++ b/tests/specs/argumentParserSpec.cfc @@ -0,0 +1,68 @@ +component extends=testbox.system.BaseSpec { + + function beforeAll() { + var config = new lib.config(); + argumentParser = new lib.parsers.arguments( config ); + makePublic( argumentParser, 'flatten' ); + makePublic( argumentParser, 'parseSimpleValue' ); + } + + function run() { + + describe( 'The flatten() method', function() { + + it( 'serializes strings', function() { + var out = argumentParser.flatten( { a: 'b' } ); + expect( out ).toBe( [ { name: 'a', value: 'b' } ] ); + } ); + + it( 'serializes integers', function() { + var out = argumentParser.flatten( { a: 1 } ); + expect( out ).toBe( [ { name: 'a', value: '1' } ] ); + } ); + + it( 'serializes booleans', function() { + var out = argumentParser.flatten( { a: true } ); + expect( out ).toBe( [ { name: 'a', value: 'true' } ] ); + } ); + + it( 'serializes structs', function() { + var out = argumentParser.flatten( { a: { b: 1, c: 'd' } } ); + var expected = [ { name: 'a[b]', value: '1' }, { name: 'a[c]', value: 'd' } ]; + expect( out ).toBe( expected ); + } ); + + it( 'serializes simple value arrays', function() { + var out = argumentParser.flatten( { a: [ 'b', 'c' ] } ); + var expected = [ { name: 'a[0]', value: 'b' }, { name: 'a[1]', value: 'c' } ]; + expect( out ).toBe( expected ); + } ); + + it( 'serializes arrays of structs', function() { + var out = argumentParser.flatten( { a: [ { b: 1 }, { b: 2 } ] } ); + var expected = [ { name: 'a[0][b]', value: '1' }, { name: 'a[1][b]', value: '2' } ]; + expect( out ).toBe( expected ); + } ); + + } ); + + describe( 'The parseSimpleValue() method', function() { + + it( 'converts cfml dates to unix timestamps', function() { + var datetime = now(); + datetime.setTime( javacast( 'long', '1521518884000' ) ); + var parsed = argumentParser.parseSimpleValue( datetime, 'timestamp' ); + expect( parsed ).toBe( 1521518884 ); + } ); + + it( 'converts strings that can be interpreted as dates to unix timestamps', function() { + var datetime = '2018-03-01T12:00:00Z'; + var parsed = argumentParser.parseSimpleValue( datetime, 'timestamp' ); + expect( parsed ).toBe( 1519905600 ); + } ); + + } ); + + } + +} diff --git a/tests/specs/configSpec.cfc b/tests/specs/configSpec.cfc new file mode 100644 index 0000000..0ea806c --- /dev/null +++ b/tests/specs/configSpec.cfc @@ -0,0 +1,67 @@ +component extends=testbox.system.BaseSpec { + + function run() { + + describe( 'The config struct passed at initialization', function() { + + var config = { apiVersion: 'latest', defaultCurrency: 'usd' }; + var stripe = new stripe( 'fake_key', config ); + var httpService = getProperty( stripe, 'httpService' ); + prepareMock( httpService ); + httpService.$( 'exec', { responseHeader: { 'Request-Id': '' }, statuscode: 200, filecontent: '{}' } ); + + afterEach( function() { + httpService.$reset(); + } ); + + it( 'can specify an api version to use', function() { + var res = stripe.accounts.retrieve(); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.headers ).toHaveLength( 2 ); + expect( httpRequest.headers[ 2 ].name ).toBeWithCase( 'Stripe-Version' ); + expect( httpRequest.headers[ 2 ].value ).toBeWithCase( 'latest' ); + } ); + + it( 'can supply a default iso currency code to requests that don''t supply it', function() { + var res = stripe.charges.create( customer = 'customer_id', amount = 2000 ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.body ).toBe( 'amount=2000¤cy=usd&customer=customer_id' ); + } ); + + it( 'doesn''t override iso currency codes passed in to requests', function() { + var res = stripe.charges.create( customer = 'customer_id', amount = 2000, currency='gbp' ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.body ).toBe( 'amount=2000¤cy=gbp&customer=customer_id' ); + } ); + + } ); + + describe( 'The convertToCents setting', function() { + + var config = { defaultCurrency: 'usd', convertToCents: true }; + var stripe = new stripe( 'fake_key', config ); + var httpService = getProperty( stripe, 'httpService' ); + prepareMock( httpService ); + httpService.$( 'exec', { responseHeader: { 'Request-Id': '' }, statuscode: 200, filecontent: '{"object":"charge","amount":2005}' } ); + + afterEach( function() { + httpService.$reset(); + } ); + + it( 'when true, currency amounts are multiplied by 100 before being passed to the Stripe API', function() { + var res = stripe.charges.create( customer = 'customer_id', amount = 20.05 ); + var httpRequest = httpService.$callLog().exec[ 1 ][ 1 ]; + expect( httpRequest.body ).toBe( 'amount=2005¤cy=usd&customer=customer_id' ); + } ); + + it( 'when true, currency amounts are divided by 100 before being returned in the response', function() { + var res = stripe.charges.create( customer = 'customer_id', amount = 20.05 ); + expect( res.content.amount ).toBe( 20.05 ); + } ); + + + } ); + + } + +} diff --git a/tests/subscriptionTests.cfc b/tests/subscriptionTests.cfc deleted file mode 100644 index 446bb30..0000000 --- a/tests/subscriptionTests.cfc +++ /dev/null @@ -1,102 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( request.apiKey ); - - } - - public void function testCreateCustomerSubscription() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", trial_period_days = 30 }; - var planObject = stripe.createPlan( argumentCollection = plan ); - var customerObject = stripe.createCustomer( source = source ); - var result = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id, trial_end = "now" ); - - debug( planObject ); - debug( customerObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( !structKeyExists( result, 'trial_end' ) || isNull( result.trial_end ), "trial was supposed to be canceled" ); - - } - - public void function testGetCustomerSubscription() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", trial_period_days = 30 }; - var planObject = stripe.createPlan( argumentCollection = plan ); - var customerObject = stripe.createCustomer( source = source ); - var subscription = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id, trial_end = "now" ); - var result = stripe.getCustomerSubscription( customer_id = customerObject.id, id = subscription.id ); - - debug( planObject ); - debug( customerObject ); - debug( subscription ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( subscription.id, result.id, "wrong subscription returned" ); - - } - - public void function testUpdateCustomerSubscription() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", trial_period_days = 30 }; - var planObject = stripe.createPlan( argumentCollection = plan ); - var customerObject = stripe.createCustomer( source = source ); - var subscription = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id, trial_end = "now" ); - var result = stripe.updateCustomerSubscription( customer_id = customerObject.id, id = subscription.id, quantity = 2 ); - - debug( planObject ); - debug( customerObject ); - debug( subscription ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( 2, result.quantity, "expected quantity to be 2" ); - - } - - public void function testCancelCustomerSubscription() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", trial_period_days = 30 }; - var planObject = stripe.createPlan( argumentCollection = plan ); - var customerObject = stripe.createCustomer( source = source ); - var subscription = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id, trial_end = "now" ); - var result = stripe.cancelCustomerSubscription( customer_id = customerObject.id, id = subscription.id, at_period_end = true ); - - debug( planObject ); - debug( customerObject ); - debug( subscription ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( result.cancel_at_period_end, "expected cancel_at_period_end to be true" ); - - } - - public void function testListCustomerSubscriptions() { - - var source = { object = 'card', number = '4242424242424242', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - var plan = { id = createUUID(), amount = 1000, interval = 'month', interval_count = 3, name = "Gold Plan", trial_period_days = 30 }; - var planObject = stripe.createPlan( argumentCollection = plan ); - var customerObject = stripe.createCustomer( source = source ); - var subscription = stripe.createCustomerSubscription( customer_id = customerObject.id, plan = planObject.id, trial_end = "now" ); - var result = stripe.listCustomerSubscriptions( customer_id = customerObject.id, include = [ 'total_count' ] ); - - debug( planObject ); - debug( customerObject ); - debug( subscription ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - -} \ No newline at end of file diff --git a/tests/test.png b/tests/test.png deleted file mode 100644 index c25c5df..0000000 Binary files a/tests/test.png and /dev/null differ diff --git a/tests/tokenTests.cfc b/tests/tokenTests.cfc deleted file mode 100644 index 3013180..0000000 --- a/tests/tokenTests.cfc +++ /dev/null @@ -1,80 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function setup() { - - stripe = new stripe.stripe( apiKey = request.apiKey ); - - } - - public void function testCreateCardToken() { - - var card = { number = '6011000990139424', exp_month = '12', exp_year = year( dateAdd( "yyyy", 1, now() ) ), cvc = '123', name = "John Doe" }; - var result = stripe.createCardToken( card ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful request" ); - assertEquals( "card", result.type, "wrong token created" ); - assertTrue( structKeyExists( result, 'id' ), "token key missing" ); - - } - - public void function testCreateCardToken_missing_required_field() { - - var card = { number = '6011000990139424', exp_year = year( dateAdd( "yyyy", 1, now() ) ), cvc = '123', name = "John Doe" }; - - try { - var result = stripe.createCardToken( card ); - fail( 'method createCardToken was supposed to throw an error' ); - } - catch( any e ) { - debug( e ); - } - - } - - public void function testCreateBankAccountToken() { - - var bank_account = { country = "US", routing_number = "110000000", account_number = "000123456789" }; - var result = stripe.createBankAccountToken( bank_account ); - - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful request" ); - assertEquals( "bank_account", result.type, "wrong token created" ); - assertTrue( structKeyExists( result, 'id' ), "token key missing" ); - - } - - public void function testCreateBankAccountToken_missing_required_field() { - - var bank_account = { routing_number = "110000000", account_number = "000123456789" }; - - try { - var result = stripe.createBankAccountToken( bank_account ); - fail( 'method createBankAccountToken was supposed to throw an error' ); - } - catch( any e ) { - debug( e ); - } - - } - - public void function testGetToken_card() { - - var card = { number = '378282246310005', exp_month = '12', exp_year = year( dateAdd( "yyyy", 1, now() ) ), cvc = '123', name = "John Doe" }; - var tokenCreate = stripe.createCardToken( card ); - var result = stripe.getToken( tokenCreate.id ); - - debug( tokenCreate ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a successful request" ); - assertEquals( tokenCreate.id, result.id, "wrong token retrieved" ); - assertEquals( right( card.number, 4 ), result.card.last4, "wrong card number retrieved" ); - - } - - - -} \ No newline at end of file diff --git a/tests/transferTests.cfc b/tests/transferTests.cfc deleted file mode 100644 index ae32888..0000000 --- a/tests/transferTests.cfc +++ /dev/null @@ -1,121 +0,0 @@ -component extends="mxunit.framework.TestCase" { - - public void function beforeTests() { - - variables.stripe = new stripe.stripe( apiKey = request.apiKey, raw = true ); - variables.source = { object = 'card', number = '4000000000000077', exp_month = '5', exp_year = year( dateAdd( "yyyy", 1, now() ) ) }; - variables.charge = stripe.createCharge( amount = 5000, source = source ); - variables.account = stripe.createAccount( email = 'test-#createUUID()#@example.com' ); - - } - - public void function testCreateTransfer() { - - var result = stripe.createTransfer( amount = 500, destination = account.id, metadata = { test = "value" } ); - - debug( charge ); - debug( account ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( "transfer", result.object, "recipient object was not returned" ); - assertEquals( { test = "value" }, result.metadata, "correct metadata was not returned" ); - - } - - public void function testGetTransfer() { - - var transferObject = stripe.createTransfer( amount = 500, destination = account.id, metadata = { test = "value" } ); - var result = stripe.getTransfer( transferObject.id ); - - debug( charge ); - debug( account ); - debug( transferObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( transferObject.id, result.id, "correct transfer object was not returned" ); - - } - - public void function testUpdateTransfer() { - - var transferObject = stripe.createTransfer( amount = 500, destination = account.id, metadata = { test = "value" } ); - var result = stripe.updateTransfer( id = transferObject.id, metadata = { test = "new value" } ); - - debug( transferObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( transferObject.id, result.id, "correct transfer object was not returned" ); - assertEquals( { test = "new value" }, result.metadata, "correct metadata was not returned" ); - - } - - public void function testListTransfers() { - - var transferObject = stripe.createTransfer( amount = 500, destination = account.id ); - var result = stripe.listTransfers( limit = 2, include = [ 'total_count' ] ); - - debug( transferObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertTrue( result.total_count >= 2, "transfers are not listed" ); - assertTrue( arrayLen( result.data ) == 2, "transfers are not listed" ); - - } - - public void function testCreateTransferReversal() { - - var transferObject = stripe.createTransfer( amount = 1000, destination = account.id ); - var result = stripe.createTransferReversal( transfer_id = transferObject.id, amount = 500, description = 'test' ); - - debug( transferObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testGetTransferReversal() { - - var transferObject = stripe.createTransfer( amount = 1000, destination = account.id ); - var transferReversal = stripe.createTransferReversal( transfer_id = transferObject.id, amount = 500, description = 'test' ); - var result = stripe.getTransferReversal( transfer_id = transferObject.id, id = transferReversal.id ); - - debug( transferObject ); - debug( transferReversal ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } - - public void function testUpdateTransferReversal() { - - var transferObject = stripe.createTransfer( amount = 500, destination = account.id, metadata = { test = "value" } ); - var transferReversal = stripe.createTransferReversal( transfer_id = transferObject.id, amount = 500, description = 'test' ); - var result = stripe.updateTransferReversal( transfer_id = transferObject.id, id = transferReversal.id, metadata = { test = "new value" } ); - - debug( transferObject ); - debug( transferReversal ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - assertEquals( { test = "new value" }, result.metadata, "correct metadata was not returned" ); - - } - - public void function testListTransferReversals() { - - var transferObject = stripe.createTransfer( amount = 500, destination = account.id ); - var result = stripe.listTransferReversals( transfer_id = transferObject.id, limit = 2, include = [ 'total_count' ] ); - - debug( transferObject ); - debug( result ); - - assertEquals( 200, result.status_code, "expected a 200 status" ); - - } -} \ No newline at end of file