user payments page
This commit is contained in:
parent
fcfd2be41b
commit
52db93ecd8
|
@ -11,22 +11,21 @@ module DiscourseSubscriptions
|
|||
|
||||
def create
|
||||
begin
|
||||
customer = ::Stripe::Customer.create(
|
||||
email: current_user.email,
|
||||
)
|
||||
customer = DiscourseSubscriptions::Customer.where(user_id: current_user.id, product_id: nil).first_or_create do |c|
|
||||
new_customer = ::Stripe::Customer.create(
|
||||
email: current_user.email
|
||||
)
|
||||
|
||||
DiscourseSubscriptions::Customer.create(
|
||||
user_id: current_user.id,
|
||||
customer_id: customer[:id],
|
||||
)
|
||||
c.customer_id = new_customer[:id]
|
||||
end
|
||||
|
||||
payment = ::Stripe::PaymentIntent.create(
|
||||
payment_method_types: ['card'],
|
||||
payment_method: params[:payment_method],
|
||||
amount: params[:amount],
|
||||
currency: params[:currency],
|
||||
confirm: true,
|
||||
customer: customer[:id],
|
||||
customer: customer[:customer_id],
|
||||
confirm: true
|
||||
)
|
||||
|
||||
render_json_dump payment
|
||||
|
@ -34,7 +33,7 @@ module DiscourseSubscriptions
|
|||
rescue ::Stripe::InvalidRequestError => e
|
||||
render_json_error e.message
|
||||
rescue ::Stripe::CardError => e
|
||||
render_json_error 'Card Declined'
|
||||
render_json_error I18n.t('discourse_subscriptions.card.declined')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiscourseSubscriptions
|
||||
module User
|
||||
class PaymentsController < ::ApplicationController
|
||||
include DiscourseSubscriptions::Stripe
|
||||
before_action :set_api_key
|
||||
requires_login
|
||||
|
||||
def index
|
||||
begin
|
||||
customer = DiscourseSubscriptions::Customer.find_by(user_id: current_user.id, product_id: nil)
|
||||
|
||||
data = []
|
||||
|
||||
if customer.present?
|
||||
payments = ::Stripe::PaymentIntent.list(customer: customer[:customer_id])
|
||||
data = payments[:data]
|
||||
end
|
||||
|
||||
render_json_dump data
|
||||
|
||||
rescue ::Stripe::InvalidRequestError => e
|
||||
render_json_error e.message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: "donation-list",
|
||||
hasSubscriptions: Ember.computed.notEmpty("subscriptions"),
|
||||
hasCharges: Ember.computed.notEmpty("charges")
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
import EmberObject from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
const Payment = Discourse.Model.extend({
|
||||
const Payment = EmberObject.extend({
|
||||
save() {
|
||||
const data = {
|
||||
payment_method: this.payment_method,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import EmberObject from "@ember/object";
|
||||
import computed from "discourse-common/utils/decorators";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
const Plan = Discourse.Model.extend({
|
||||
const Plan = EmberObject.extend({
|
||||
amountDollars: Ember.computed("amount", {
|
||||
get() {
|
||||
return parseFloat(this.get("amount") / 100).toFixed(2);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import EmberObject from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
const Product = Discourse.Model.extend({});
|
||||
const Product = EmberObject.extend({});
|
||||
|
||||
Product.reopenClass({
|
||||
findAll() {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import EmberObject from "@ember/object";
|
||||
import computed from "discourse-common/utils/decorators";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
const UserPayment = EmberObject.extend({
|
||||
@computed("amount")
|
||||
amountDollars(amount){
|
||||
return parseFloat(amount / 100).toFixed(2);
|
||||
}
|
||||
});
|
||||
|
||||
UserPayment.reopenClass({
|
||||
findAll() {
|
||||
return ajax("/s/user/payments", { method: "get" }).then(result =>
|
||||
result.map(payment => {
|
||||
return UserPayment.create(payment);
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default UserPayment;
|
|
@ -1,8 +1,9 @@
|
|||
import EmberObject from "@ember/object";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
|
||||
|
||||
const UserSubscription = Discourse.Model.extend({
|
||||
const UserSubscription = EmberObject.extend({
|
||||
@computed("status")
|
||||
canceled(status) {
|
||||
return status === "canceled";
|
||||
|
|
|
@ -7,7 +7,7 @@ export default Route.extend({
|
|||
const product_id = params["subscription-id"];
|
||||
|
||||
const product = Product.find(product_id);
|
||||
const plans = Plan.findAll({ product_id: product_id });
|
||||
const plans = Plan.findAll({ product_id });
|
||||
|
||||
return Ember.RSVP.hash({ plans, product });
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Route from "@ember/routing/route";
|
||||
import Invoice from "discourse/plugins/discourse-subscriptions/discourse/models/invoice";
|
||||
import UserPayment from "discourse/plugins/discourse-subscriptions/discourse/models/user-payment";
|
||||
|
||||
export default Route.extend({
|
||||
templateName: "user/billing/payments",
|
||||
|
||||
model() {
|
||||
return Invoice.findAll();
|
||||
return UserPayment.findAll();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
{{#if hasSubscriptions}}
|
||||
<div class="subscription-list">
|
||||
<div class="underline">{{i18n 'discourse_donations.donations.subscriptions'}}</div>
|
||||
<ul>
|
||||
{{#each subscriptions as |s|}}
|
||||
<li>{{donation-row subscription=s.subscription customer=customer new=s.new}}</li>
|
||||
{{#if s.invoices}}
|
||||
<ul>
|
||||
{{#each s.invoices as |invoice|}}
|
||||
<li>{{donation-row invoice=invoice customer=customer new=s.new}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if hasCharges}}
|
||||
<div class="charge-list">
|
||||
<div class='underline'>{{i18n 'discourse_donations.donations.charges'}}</div>
|
||||
<ul>
|
||||
{{#each charges as |charge|}}
|
||||
<li>{{donation-row charge=charge customer=customer new=charge.new}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,22 +1,15 @@
|
|||
|
||||
{{#if model}}
|
||||
<table class="topic-list">
|
||||
<table class="table discourse-subscriptions-user-table">
|
||||
<thead>
|
||||
<th>{{i18n 'discourse_subscriptions.user.billing.invoices.amount'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.billing.invoices.number'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.billing.invoices.created_at'}}</th>
|
||||
<th></th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.payments.id'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.payments.amount'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.payments.created_at'}}</th>
|
||||
</thead>
|
||||
{{#each model as |invoice|}}
|
||||
{{#each model as |payment|}}
|
||||
<tr>
|
||||
<td>{{invoice.amount_paid}}</td>
|
||||
<td>{{invoice.number}}</td>
|
||||
<td>{{format-unix-date invoice.created}}</td>
|
||||
<td class="td-right">
|
||||
<a href="{{invoice.invoice_pdf}}" class="btn btn-icon">
|
||||
{{d-icon "download"}}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{payment.id}}</td>
|
||||
<td>{{format-currency payment.currency payment.amountDollars}}</td>
|
||||
<td>{{format-unix-date payment.created}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
|
|
|
@ -1,34 +1,32 @@
|
|||
{{#d-section pageClass="user-subscriptions" class="user-content"}}
|
||||
{{#if model}}
|
||||
<table class="table discourse-subscriptions-user-table">
|
||||
<thead>
|
||||
<th>{{i18n 'discourse_subscriptions.user.subscriptions.id'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.plans.product'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.plans.rate'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.subscriptions.status'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.subscriptions.created_at'}}</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
{{#each model as |subscription|}}
|
||||
<tr>
|
||||
<td>{{subscription.id}}</td>
|
||||
<td>{{subscription.product.name}}</td>
|
||||
<td>{{subscription.plan.subscriptionRate}}</td>
|
||||
<td>{{subscription.status}}</td>
|
||||
<td>{{format-unix-date subscription.created}}</td>
|
||||
<td class="td-right">
|
||||
{{#if subscription.loading}}
|
||||
{{loading-spinner size="small"}}
|
||||
{{else}}
|
||||
{{d-button disabled=subscription.canceled label="cancel" action=(route-action "cancelSubscription" subscription) icon="times"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{else}}
|
||||
<div class="alert alert-info">
|
||||
{{i18n 'discourse_subscriptions.user.subscriptions_help'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/d-section}}
|
||||
{{#if model}}
|
||||
<table class="table discourse-subscriptions-user-table">
|
||||
<thead>
|
||||
<th>{{i18n 'discourse_subscriptions.user.subscriptions.id'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.plans.product'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.plans.rate'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.subscriptions.status'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.user.subscriptions.created_at'}}</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
{{#each model as |subscription|}}
|
||||
<tr>
|
||||
<td>{{subscription.id}}</td>
|
||||
<td>{{subscription.product.name}}</td>
|
||||
<td>{{subscription.plan.subscriptionRate}}</td>
|
||||
<td>{{subscription.status}}</td>
|
||||
<td>{{format-unix-date subscription.created}}</td>
|
||||
<td class="td-right">
|
||||
{{#if subscription.loading}}
|
||||
{{loading-spinner size="small"}}
|
||||
{{else}}
|
||||
{{d-button disabled=subscription.canceled label="cancel" action=(route-action "cancelSubscription" subscription) icon="times"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{else}}
|
||||
<div class="alert alert-info">
|
||||
{{i18n 'discourse_subscriptions.user.subscriptions_help'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -50,19 +50,11 @@ en:
|
|||
validate:
|
||||
payment_options:
|
||||
required: Please select a payment option.
|
||||
one_time:
|
||||
heading:
|
||||
payment: Make a Payment
|
||||
success: Thank you!
|
||||
payment:
|
||||
optional: Optional
|
||||
receipt_info: A receipt is sent to this email address
|
||||
your_information: Your information
|
||||
payment_information: Payment information
|
||||
payment_confirmation: Confirm information
|
||||
amount: Amount
|
||||
payment_intent_id: Payment ID
|
||||
user:
|
||||
payments:
|
||||
id: Payment ID
|
||||
amount: Amount
|
||||
created_at: Created
|
||||
payments_help: There are no payments
|
||||
plans:
|
||||
rate: Rate
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
en:
|
||||
discourse_subscriptions:
|
||||
customer_not_found: Customer not found
|
||||
card:
|
||||
declined: Card Declined
|
||||
|
|
|
@ -13,6 +13,7 @@ DiscourseSubscriptions::Engine.routes.draw do
|
|||
end
|
||||
|
||||
namespace :user do
|
||||
resources :payments, only: [:index]
|
||||
resources :subscriptions, only: [:index, :destroy]
|
||||
end
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ after_initialize do
|
|||
"../app/controllers/admin/plans_controller",
|
||||
"../app/controllers/admin/products_controller",
|
||||
"../app/controllers/admin/subscriptions_controller",
|
||||
"../app/controllers/user/payments_controller",
|
||||
"../app/controllers/user/subscriptions_controller",
|
||||
"../app/controllers/customers_controller",
|
||||
"../app/controllers/invoices_controller",
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
module DiscourseSubscriptions
|
||||
RSpec.describe User::PaymentsController do
|
||||
it 'is a subclass of ApplicationController' do
|
||||
expect(DiscourseSubscriptions::User::PaymentsController < ::ApplicationController).to eq(true)
|
||||
end
|
||||
|
||||
context "not authenticated" do
|
||||
it "does not get the payment intents" do
|
||||
::Stripe::PaymentIntent.expects(:list).never
|
||||
get "/s/user/payments.json"
|
||||
end
|
||||
end
|
||||
|
||||
context "authenticated" do
|
||||
let(:user) { Fabricate(:user, email: 'zasch@example.com') }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
Fabricate(:customer, customer_id: 'c_345678', user_id: user.id)
|
||||
end
|
||||
|
||||
it "gets payment intents" do
|
||||
::Stripe::PaymentIntent.expects(:list).with(
|
||||
customer: 'c_345678'
|
||||
)
|
||||
|
||||
get "/s/user/payments.json"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,31 +0,0 @@
|
|||
import componentTest from "helpers/component-test";
|
||||
import { stubStripe } from "discourse/plugins/discourse-subscriptions/helpers/stripe";
|
||||
|
||||
moduleForComponent("stripe-card", { integration: true });
|
||||
|
||||
componentTest("Discourse Patrons stripe card success", {
|
||||
template: `{{stripe-card handleConfirmStripeCard=onSubmit billing=billing}}`,
|
||||
|
||||
beforeEach() {
|
||||
stubStripe();
|
||||
|
||||
this.set(
|
||||
"billing",
|
||||
Ember.Object.create({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: ""
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.set("onSubmit", () => {
|
||||
assert.ok(true, "payment method created");
|
||||
});
|
||||
|
||||
await click(".btn-payment");
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue