Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d857cd7fed | ||
|
|
93c0b02a45 | ||
|
|
52468d17b9 | ||
|
|
17f5fd2841 | ||
|
|
255fd7f636 | ||
|
|
824f61aab1 | ||
|
|
aff99cb20b | ||
|
|
ccea023274 | ||
|
|
2e91a8e3a3 | ||
|
|
6ab3de9fca | ||
|
|
3c431b9915 | ||
|
|
1853bdd6f5 | ||
|
|
8e700c4650 | ||
|
|
4933ae2233 | ||
|
|
3bdef7f582 | ||
|
|
518cedab1b | ||
|
|
e981471217 | ||
|
|
a43f79d7a0 | ||
|
|
be122744a3 | ||
|
|
b13cbe663e | ||
|
|
f870634afd | ||
|
|
6a216d81e5 | ||
|
|
2f33bdf76b | ||
|
|
545e962291 | ||
|
|
904cd4d1d9 | ||
|
|
49bcd392d4 | ||
|
|
d531684373 | ||
|
|
377d873f2b | ||
|
|
f942223eac | ||
|
|
83b4e72ff4 | ||
|
|
b80440206b |
@@ -16,7 +16,7 @@ Accept donations in Discourse! Integrates with [Stripe](https://stripe.com).
|
||||
|
||||
**This is an experimental feature.** A user can create a new account if they makes a successful donation. Enable this in settings. When a user is not logged in, they will be asked to enter details for a new user account. This feature doesn't support mandatory custom user fields yet.
|
||||
|
||||
## Testing
|
||||
## Test Suite
|
||||
|
||||
* To run the rails specs, install the plugin and run `bundle exec rake plugin:spec[discourse-donations]` in the discourse root directory.
|
||||
* To run qunit tests: `MODULE='Acceptance: Discourse Donations' bundle exec rake qunit:test[20000]`.
|
||||
@@ -28,9 +28,33 @@ Accept donations in Discourse! Integrates with [Stripe](https://stripe.com).
|
||||
|
||||
## TODO
|
||||
|
||||
* Test card fails
|
||||
* Handle custom fields
|
||||
* Acceptance test in RSpec not qunit.
|
||||
|
||||
## Tested Credit Card Numbers
|
||||
|
||||
These numbers can be used in test mode to simulate a transaction. For more information see the [Stripe docs](https://stripe.com/docs/testing).
|
||||
|
||||
Card numbers in **bold** have beed tested.
|
||||
|
||||
* **4000 0000 0000 0077** Charge succeeds and funds will be added directly to your available balance (bypassing your pending balance).
|
||||
* **4000 0000 0000 0093** Charge succeeds and domestic pricing is used (other test cards use international pricing). This card is only significant in countries with split pricing.
|
||||
* **4000 0000 0000 0010** The address_line1_check and address_zip_check verifications fail. If your account is blocking payments that fail postal code validation, the charge is declined.
|
||||
* **4000 0000 0000 0028** Charge succeeds but the address_line1_check verification fails.
|
||||
* **4000 0000 0000 0036** The address_zip_check verification fails. If your account is blocking payments that fail postal code validation, the charge is declined.
|
||||
* **4000 0000 0000 0044** Charge succeeds but the address_zip_check and address_line1_check verifications are both unavailable.
|
||||
* **4000 0000 0000 0101** If a CVC number is provided, the cvc_check fails. If your account is blocking payments that fail CVC code validation, the charge is declined.
|
||||
* **4000 0000 0000 0341** Attaching this card to a Customer object succeeds, but attempts to charge the customer fail.
|
||||
* **4000 0000 0000 9235** Charge succeeds with a risk_level of elevated and placed into review.
|
||||
* **4000 0000 0000 0002** Charge is declined with a card_declined code.
|
||||
* **4100 0000 0000 0019** Charge is declined with a card_declined code and a fraudulent reason.
|
||||
* **4000 0000 0000 0127** Charge is declined with an incorrect_cvc code.
|
||||
* **4000 0000 0000 0069** Charge is declined with an expired_card code.
|
||||
* **4000 0000 0000 0119** Charge is declined with a processing_error code.
|
||||
* 4242 4242 4242 4241 Charge is declined with an incorrect_number code as the card number fails the Luhn check.
|
||||
|
||||
## Warranty
|
||||
|
||||
This software comes with no warranty of any kind.
|
||||
>>>>>>> master
|
||||
|
||||
@@ -7,7 +7,7 @@ module DiscourseDonations
|
||||
skip_before_filter :verify_authenticity_token, only: [:create]
|
||||
|
||||
def create
|
||||
params.permit(:name, :username, :email, :password, :stripeToken, :amount, :create_account)
|
||||
params.permit(:name, :username, :email, :password, :stripeToken, :amount, :plan, :create_account)
|
||||
|
||||
output = { 'messages' => [], 'rewards' => [] }
|
||||
|
||||
@@ -30,7 +30,11 @@ module DiscourseDonations
|
||||
payment = DiscourseDonations::Stripe.new(secret_key, stripe_options)
|
||||
|
||||
begin
|
||||
charge = payment.charge(email, params)
|
||||
if params['amount'].present?
|
||||
charge = payment.subscribe(email, params.merge(plan: params[:amount]))
|
||||
else
|
||||
charge = payment.subscribe(email, params)
|
||||
end
|
||||
rescue ::Stripe::CardError => e
|
||||
err = e.json_body[:error]
|
||||
|
||||
@@ -42,7 +46,9 @@ module DiscourseDonations
|
||||
render(:json => output) and return
|
||||
end
|
||||
|
||||
if charge['paid'] == true
|
||||
if charge['paid'] == true || charge['status'] == 'active'
|
||||
output['success'] = true
|
||||
|
||||
output['messages'] << I18n.t('donations.payment.success')
|
||||
|
||||
output['rewards'] << { type: :group, name: group_name } if group_name
|
||||
|
||||
@@ -24,14 +24,8 @@ module DiscourseDonations
|
||||
end
|
||||
|
||||
def subscribe(email, opts)
|
||||
customer = ::Stripe::Customer.create(
|
||||
email: email,
|
||||
source: opts[:stripeToken]
|
||||
)
|
||||
@subscription = ::Stripe::Subscription.create(
|
||||
customer: customer.id,
|
||||
plan: opts[:plan]
|
||||
)
|
||||
customer = ::Stripe::Customer.create(email: email, source: opts[:stripeToken])
|
||||
@subscription = ::Stripe::Subscription.create(customer: customer.id, plan: opts[:plan])
|
||||
@subscription
|
||||
end
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { getRegister } from 'discourse-common/lib/get-owner';
|
||||
|
||||
const { computed: { alias }, observer } = Ember
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
||||
routing: Ember.inject.service('-routing'),
|
||||
params: alias('routing.router.currentState.routerJsState.fullQueryParams'),
|
||||
|
||||
donateAmounts: [
|
||||
{ value: 1, name: '$1.00'},
|
||||
{ value: 2, name: '$2.00'},
|
||||
{ value: 5, name: '$5.00'},
|
||||
{ value: 10, name: '$10.00'},
|
||||
{ value: 20, name: '$20.00'},
|
||||
{ value: 50, name: '$50.00'}
|
||||
{ value: 'consumer-defender-1', name: 'Consumer Defender: $1.00/quarter'},
|
||||
{ value: 'consumer-defender-3', name: 'Consumer Defender: $3.00/quarter'},
|
||||
{ value: 'consumer-defender-6', name: 'Consumer Defender: $6.00/quarter'},
|
||||
{ value: 'consumer-defender-9', name: 'Consumer Defender: $9.00/quarter'},
|
||||
{ value: 'consumer-defender-12', name: 'Consumer Defender: $12.00/quarter'},
|
||||
],
|
||||
result: [],
|
||||
amount: null,
|
||||
@@ -16,6 +21,10 @@ export default Ember.Component.extend({
|
||||
transactionInProgress: null,
|
||||
settings: null,
|
||||
|
||||
consumerDefenderWithMembership: function() {
|
||||
return this.get('params.plan') == 'consumer-defender-with-membership';
|
||||
}.property('params'),
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
this.set('anon', (Discourse.User.current() == null));
|
||||
@@ -52,28 +61,39 @@ export default Ember.Component.extend({
|
||||
submitStripeCard() {
|
||||
let self = this;
|
||||
|
||||
self.set('transactionInProgress', true);
|
||||
|
||||
this.get('stripe').createToken(this.get('card')).then(data => {
|
||||
|
||||
self.set('result', []);
|
||||
|
||||
if (data.error) {
|
||||
self.set('result', data.error.message);
|
||||
self.set('transactionInProgress', false);
|
||||
}
|
||||
else {
|
||||
self.set('transactionInProgress', true);
|
||||
|
||||
let params = {
|
||||
stripeToken: data.token.id,
|
||||
amount: self.get('amount') * 100,
|
||||
email: self.get('email'),
|
||||
username: self.get('username'),
|
||||
create_account: self.get('create_accounts')
|
||||
};
|
||||
|
||||
if(this.get('params.plan')) {
|
||||
params.plan = this.get('params.plan');
|
||||
}
|
||||
else {
|
||||
params.amount = self.get('amount');
|
||||
}
|
||||
|
||||
if(!self.get('paymentSuccess')) {
|
||||
ajax('/charges', { data: params, method: 'post' }).then(data => {
|
||||
self.concatMessages(data.messages);
|
||||
self.endTranscation();
|
||||
if(data.success) {
|
||||
self.setSuccess();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,41 @@
|
||||
<form id="payment-form" class="form-horizontal">
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="card-element">
|
||||
{{i18n 'discourse_donations.amount'}}
|
||||
{{settings.discourse_donations_currency}}
|
||||
</label>
|
||||
<div class="controls controls-dropdown">
|
||||
{{combo-box valueAttribute="value" content=donateAmounts value=amount}}
|
||||
{{#if consumerDefenderWithMembership}}
|
||||
|
||||
<h3>[$25/quarter] Consumer Defender + CHOICE Membership</h3>
|
||||
|
||||
<ul>
|
||||
<li>Choice.community Access</li>
|
||||
<li>Defender Badge (while supplies last)</li>
|
||||
<li>Priority Expert Advice</li>
|
||||
<li>All the benefits of CHOICE membership</li>
|
||||
<li>CHOICE Members Only area</li>
|
||||
</ul>
|
||||
|
||||
{{else}}
|
||||
|
||||
<h3>[Pay what you want/quarter] Consumer Defender only</h3>
|
||||
|
||||
<ul>
|
||||
<li>Choice.community Access</li>
|
||||
<li>Defender Badge (while supplies last)</li>
|
||||
<li>Priority Expert Advice</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="card-element">
|
||||
{{i18n 'discourse_donations.amount'}}
|
||||
{{settings.discourse_donations_currency}}
|
||||
</label>
|
||||
<div class="controls controls-dropdown">
|
||||
{{combo-box valueAttribute="value" content=donateAmounts value=amount}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/if}}
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="control-group" style="width: 550px;">
|
||||
<label class="control-label" for="card-element">{{i18n 'discourse_donations.card'}}</label>
|
||||
@@ -47,15 +74,22 @@
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label"></label>
|
||||
<div class="controls">By making a purchase you agree to our <a href="https://www.choice.com.au/policies-terms-and-conditions/terms-and-conditions">terms and conditions</a></div>
|
||||
</div>
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{#d-button action="submitStripeCard" disabled=transactionInProgress class="btn btn-primary btn-payment"}}
|
||||
{{#if create_accounts}}
|
||||
{{i18n 'discourse_donations.submit_with_create_account'}}
|
||||
{{else}}
|
||||
{{i18n 'discourse_donations.submit'}}
|
||||
{{/if}}
|
||||
{{/d-button}}
|
||||
{{#unless paymentSuccess}}
|
||||
{{#d-button action="submitStripeCard" disabled=transactionInProgress class="btn btn-primary btn-payment"}}
|
||||
{{#if create_accounts}}
|
||||
{{i18n 'discourse_donations.submit_with_create_account'}}
|
||||
{{else}}
|
||||
{{i18n 'discourse_donations.submit'}}
|
||||
{{/if}}
|
||||
{{/d-button}}
|
||||
{{/unless}}
|
||||
|
||||
{{#if transactionInProgress}}
|
||||
{{loading-spinner size="small"}}
|
||||
@@ -65,8 +99,10 @@
|
||||
<p>{{{message}}}</p>
|
||||
{{/each}}
|
||||
|
||||
{{#if success}}
|
||||
<p>{{i18n 'discourse_donations.messages.success'}}</p>
|
||||
{{#if paymentSuccess}}
|
||||
{{#if create_accounts}}
|
||||
<p>Please <strong>check your email</strong> to get your log in link.</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<a href="/donate">
|
||||
{{i18n 'discourse_donations.nav_item'}}
|
||||
</a>
|
||||
{{#if settings.discourse_donations_show_extra_nav_item}}
|
||||
<a href="/donate">
|
||||
{{i18n 'discourse_donations.nav_item'}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
@@ -8,4 +8,4 @@ en:
|
||||
submit: Make Payment
|
||||
submit_with_create_account: Make Payment and Create Account
|
||||
messages:
|
||||
success: Thank you for your donation!
|
||||
success: "Thank you, your payment has been successful."
|
||||
|
||||
@@ -8,6 +8,8 @@ en:
|
||||
discourse_donations_hide_zip_code: Hide Zip Code
|
||||
discourse_donations_reward_badge_name: Grant this badge to user when a payment is successful
|
||||
discourse_donations_reward_group_name: Add the user to this group when a payment is successful
|
||||
discourse_donations_show_extra_nav_item: Show extra nav item
|
||||
|
||||
en:
|
||||
donations:
|
||||
payment:
|
||||
|
||||
@@ -24,3 +24,6 @@ plugins:
|
||||
discourse_donations_reward_group_name:
|
||||
client: false
|
||||
default: 'Donation'
|
||||
discourse_donations_show_extra_nav_item:
|
||||
client: true
|
||||
default: true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# name: discourse-donations
|
||||
# about: Integrating Discourse with Stripe for donations
|
||||
# version: 1.11.0
|
||||
# version: 2.3.2
|
||||
# url: https://github.com/choiceaustralia/discourse-donations
|
||||
# authors: Rimian Perkins
|
||||
|
||||
|
||||
@@ -24,24 +24,24 @@ module DiscourseDonations
|
||||
end
|
||||
|
||||
it 'whitelists the params' do
|
||||
params = { create_account: 'true', email: '[email protected]', password: 'secret', username: 'mr-pink', name: 'kirsten', amount: 100, stripeToken: 'rrurrrurrrrr' }
|
||||
should permit(:name, :username, :email, :password, :create_account).for(:create, params: params)
|
||||
params = { create_account: 'true', email: '[email protected]', password: 'secret', username: 'mr-pink', name: 'kirsten', amount: 100, plan: 'some-plan-id', stripeToken: 'rrurrrurrrrr' }
|
||||
should permit(:name, :username, :email, :password, :plan, :create_account).for(:create, params: params)
|
||||
end
|
||||
|
||||
it 'responds ok for anonymous users' do
|
||||
post :create, { email: '[email protected]' }
|
||||
post :create, { email: '[email protected]', amount: 1000 }
|
||||
expect(body['messages']).to include(I18n.t('donations.payment.success'))
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'does not expect a username or email if accounts are not being created' do
|
||||
current_user = log_in(:coding_horror)
|
||||
post :create, { create_account: 'false' }
|
||||
post :create, { create_account: 'false', amount: 1200 }
|
||||
expect(body['messages']).to include(I18n.t('donations.payment.success'))
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
describe 'create accounts' do
|
||||
describe 'create accounts setting' do
|
||||
describe 'create acccount disabled' do
|
||||
let(:params) { { amount: 100, stripeToken: 'rrurrrurrrrr-rrruurrrr' } }
|
||||
|
||||
@@ -113,6 +113,16 @@ module DiscourseDonations
|
||||
end
|
||||
end
|
||||
|
||||
describe 'subscriptions and charges' do
|
||||
let(:params) { { create_account: 'true', email: '[email protected]', password: 'secret', name: 'dood', username: 'mr-dood' } }
|
||||
|
||||
it 'creates a subscription' do
|
||||
skip 'not done yet'
|
||||
DiscourseDonations::Stripe.expects(:subscribe).returns('') #.with(params[:email], plan: 'quarterly-description')
|
||||
post :create, params.merge(plan: 'quarterly-description')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'rewards' do
|
||||
let(:body) { JSON.parse(response.body) }
|
||||
let(:stripe) { ::Stripe::Charge }
|
||||
@@ -125,7 +135,7 @@ module DiscourseDonations
|
||||
end
|
||||
|
||||
describe 'new user' do
|
||||
let(:params) { { create_account: 'true', email: '[email protected]', password: 'secret', name: 'dood', username: 'mr-dood' } }
|
||||
let(:params) { { amount: 5000, create_account: 'true', email: '[email protected]', password: 'secret', name: 'dood', username: 'mr-dood' } }
|
||||
|
||||
before { SiteSetting.stubs(:discourse_donations_enable_create_accounts).returns(true) }
|
||||
|
||||
@@ -150,7 +160,7 @@ module DiscourseDonations
|
||||
end
|
||||
|
||||
include_examples 'no rewards' do
|
||||
let(:params) { nil }
|
||||
let(:params) { { amount: 4500 } }
|
||||
|
||||
before do
|
||||
stripe.stubs(:create).returns({ 'paid' => true })
|
||||
@@ -164,6 +174,7 @@ module DiscourseDonations
|
||||
let(:badge_name) { 'Beanie' }
|
||||
let!(:grp) { Fabricate(:group, name: group_name) }
|
||||
let!(:badge) { Fabricate(:badge, name: badge_name) }
|
||||
let(:params) { { amount: 4500 } }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(:discourse_donations_reward_group_name).returns(group_name)
|
||||
@@ -172,12 +183,12 @@ module DiscourseDonations
|
||||
end
|
||||
|
||||
it 'awards a group' do
|
||||
post :create
|
||||
post :create, params
|
||||
expect(body['rewards']).to include({'type' => 'group', 'name' => group_name})
|
||||
end
|
||||
|
||||
it 'awards a badge' do
|
||||
post :create
|
||||
post :create, params
|
||||
expect(body['rewards']).to include({'type' => 'badge', 'name' => badge_name})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,75 @@ require 'fakeweb'
|
||||
|
||||
#TODO register some fixtures
|
||||
|
||||
FakeWeb.register_uri(:post, 'https://api.stripe.com/v1/subscriptions',
|
||||
:body => '{
|
||||
"id": "sub_AiD01tPjRSDFYu",
|
||||
"object": "subscription",
|
||||
"application_fee_percent": null,
|
||||
"cancel_at_period_end": false,
|
||||
"canceled_at": null,
|
||||
"created": 1495582751,
|
||||
"current_period_end": 1503531551,
|
||||
"current_period_start": 1495582751,
|
||||
"customer": "cus_AiD0rheP5e5fWO",
|
||||
"discount": null,
|
||||
"ended_at": null,
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_1AMmapEfVxQsvRbHOwo0LqL7",
|
||||
"object": "subscription_item",
|
||||
"created": 1495582752,
|
||||
"plan": {
|
||||
"id": "membership",
|
||||
"object": "plan",
|
||||
"amount": 2500,
|
||||
"created": 1495503265,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 3,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"name": "Membership",
|
||||
"statement_descriptor": null,
|
||||
"trial_period_days": null
|
||||
},
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_AiD01tPjRFFBYu"
|
||||
},
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"plan": {
|
||||
"id": "membership",
|
||||
"object": "plan",
|
||||
"amount": 2500,
|
||||
"created": 1495503265,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 3,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"name": "Membership",
|
||||
"statement_descriptor": null,
|
||||
"trial_period_days": null
|
||||
},
|
||||
"quantity": 1,
|
||||
"start": 1495582751,
|
||||
"status": "active",
|
||||
"tax_percent": null,
|
||||
"trial_end": null,
|
||||
"trial_start": null
|
||||
}'
|
||||
)
|
||||
|
||||
FakeWeb.register_uri(:post, 'https://api.stripe.com/v1/customers',
|
||||
:body => '{
|
||||
"id": "cus_AJqrL4OU1sffPl",
|
||||
|
||||
Reference in New Issue
Block a user