mirror of
https://github.com/discourse/discourse-subscriptions.git
synced 2025-07-08 23:32:44 +00:00
add basic invoices page
This commit is contained in:
parent
b7a3be9344
commit
87c83abcd3
37
app/controllers/invoices_controller.rb
Normal file
37
app/controllers/invoices_controller.rb
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module DiscoursePatrons
|
||||||
|
class InvoicesController < ::ApplicationController
|
||||||
|
include DiscoursePatrons::Stripe
|
||||||
|
|
||||||
|
requires_login
|
||||||
|
|
||||||
|
before_action :set_api_key
|
||||||
|
|
||||||
|
def index
|
||||||
|
begin
|
||||||
|
customer = find_customer
|
||||||
|
|
||||||
|
if viewing_own_invoices && customer.present?
|
||||||
|
invoices = ::Stripe::Invoice.list(customer: customer.customer_id)
|
||||||
|
|
||||||
|
render_json_dump invoices.data
|
||||||
|
else
|
||||||
|
render_json_dump []
|
||||||
|
end
|
||||||
|
rescue ::Stripe::InvalidRequestError => e
|
||||||
|
return render_json_error e.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def viewing_own_invoices
|
||||||
|
current_user.id == params[:user_id].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_customer
|
||||||
|
DiscoursePatrons::Customer.find_user(current_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
assets/javascripts/discourse/models/invoice.js.es6
Normal file
19
assets/javascripts/discourse/models/invoice.js.es6
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
|
const Invoice = Discourse.Model.extend({
|
||||||
|
@computed("created")
|
||||||
|
createdFormatted(created) {
|
||||||
|
return moment.unix(created).format();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Invoice.reopenClass({
|
||||||
|
findAll() {
|
||||||
|
return ajax("/patrons/invoices", { method: "get" }).then(result =>
|
||||||
|
result.map(invoice => Invoice.create(invoice))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Invoice;
|
@ -1,6 +1,8 @@
|
|||||||
|
import Invoice from "discourse/plugins/discourse-patrons/discourse/models/invoice";
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
model() {
|
model() {
|
||||||
return {};
|
return Invoice.findAll();
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
<h3>{{i18n 'discourse_patrons.admin.dashboard.title'}}</h3>
|
<h3>{{i18n 'discourse_patrons.admin.dashboard.title'}}</h3>
|
||||||
|
|
||||||
{{#load-more selector=".discourse-patrons-admin tr" action=(action "loadMore")}}
|
{{#load-more selector=".discourse-patrons-table tr" action=(action "loadMore")}}
|
||||||
{{#if model}}
|
{{#if model}}
|
||||||
<table class="table discourse-patrons-admin">
|
<table class="table discourse-patrons-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{i18n 'discourse_patrons.admin.dashboard.table.head.user'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.dashboard.table.head.user'}}</th>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<table class="table discourse-patrons-admin">
|
<table class="table discourse-patrons-table">
|
||||||
<thead>
|
<thead>
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.plan_id'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.plan_id'}}</th>
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname.title'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname.title'}}</th>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
{{#if model}}
|
{{#if model}}
|
||||||
<table class="table discourse-patrons-admin">
|
<table class="table discourse-patrons-table">
|
||||||
<thead>
|
<thead>
|
||||||
<th>{{i18n 'discourse_patrons.admin.products.product.name'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.products.product.name'}}</th>
|
||||||
<th>{{i18n 'discourse_patrons.admin.products.product.created_at'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.products.product.created_at'}}</th>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<h4>{{i18n 'discourse_patrons.admin.plans.title'}}</h4>
|
<h4>{{i18n 'discourse_patrons.admin.plans.title'}}</h4>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<table class="table discourse-patrons-admin">
|
<table class="table discourse-patrons-table">
|
||||||
<thead>
|
<thead>
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.nickname'}}</th>
|
||||||
<th>{{i18n 'discourse_patrons.admin.plans.plan.interval'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.plans.plan.interval'}}</th>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<table class="table discourse-patrons-admin">
|
<table class="table discourse-patrons-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{i18n 'discourse_patrons.admin.subscriptions.subscription.customer'}}</th>
|
<th>{{i18n 'discourse_patrons.admin.subscriptions.subscription.customer'}}</th>
|
||||||
|
@ -1,3 +1,27 @@
|
|||||||
|
|
||||||
|
<h3>{{i18n 'discourse_patrons.user.billing.title'}}</h3>
|
||||||
|
|
||||||
user/billing
|
{{#if model}}
|
||||||
|
<table class="topic-list">
|
||||||
|
<thead>
|
||||||
|
<th>{{i18n 'discourse_patrons.user.billing.invoices.amount'}}</th>
|
||||||
|
<th>{{i18n 'discourse_patrons.user.billing.invoices.number'}}</th>
|
||||||
|
<th>{{i18n 'discourse_patrons.user.billing.invoices.created_at'}}</th>
|
||||||
|
<th></th>
|
||||||
|
</thead>
|
||||||
|
{{#each model as |invoice|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{invoice.amount_paid}}</td>
|
||||||
|
<td>{{invoice.number}}</td>
|
||||||
|
<td>{{format-date invoice.createdFormatted}}</td>
|
||||||
|
<td class="td-right">
|
||||||
|
<a href="{{invoice.invoice_pdf}}" class="btn btn-icon">
|
||||||
|
{{d-icon "download"}}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
<p>{{i18n 'discourse_patrons.user.billing_help'}}</p>
|
||||||
|
{{/if}}
|
||||||
|
33
assets/stylesheets/common/discourse-patrons-layout.scss
Normal file
33
assets/stylesheets/common/discourse-patrons-layout.scss
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.discourse-patrons-section-columns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
@include breakpoint(medium) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-column {
|
||||||
|
min-width: calc(50% - 0.5em);
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint(medium) {
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,47 +10,13 @@ textarea[readonly] {
|
|||||||
border-color: #e9e9e9;
|
border-color: #e9e9e9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discourse-patrons-section-columns {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
@include breakpoint(medium) {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-column {
|
|
||||||
min-width: calc(50% - 0.5em);
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include breakpoint(medium) {
|
|
||||||
min-width: 100%;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
order: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
order: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#discourse-patrons-admin {
|
#discourse-patrons-admin {
|
||||||
.btn-right {
|
.btn-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.discourse-patrons-admin {
|
table.discourse-patrons-table {
|
||||||
.td-right {
|
.td-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,15 @@ en:
|
|||||||
navigation:
|
navigation:
|
||||||
subscribe: Subscribe
|
subscribe: Subscribe
|
||||||
billing: Billing
|
billing: Billing
|
||||||
subscribe:
|
user:
|
||||||
|
billing_help: We couldn't find a customer identifier in our system.
|
||||||
|
billing:
|
||||||
|
title: Billing
|
||||||
|
invoices:
|
||||||
|
amount: Amount
|
||||||
|
number: Invoice Number
|
||||||
|
created_at: Created
|
||||||
|
subscribe:
|
||||||
title: Subscribe
|
title: Subscribe
|
||||||
card:
|
card:
|
||||||
title: Payment
|
title: Payment
|
||||||
|
@ -13,10 +13,11 @@ DiscoursePatrons::Engine.routes.draw do
|
|||||||
end
|
end
|
||||||
|
|
||||||
resources :customers, only: [:create]
|
resources :customers, only: [:create]
|
||||||
resources :subscriptions, only: [:create]
|
resources :invoices, only: [:index]
|
||||||
|
resources :patrons, only: [:index, :create]
|
||||||
resources :plans, only: [:index]
|
resources :plans, only: [:index]
|
||||||
resources :products, only: [:index]
|
resources :products, only: [:index]
|
||||||
resources :patrons, only: [:index, :create]
|
resources :subscriptions, only: [:create]
|
||||||
|
|
||||||
get '/' => 'patrons#index'
|
get '/' => 'patrons#index'
|
||||||
end
|
end
|
||||||
|
@ -11,6 +11,7 @@ enabled_site_setting :discourse_patrons_enabled
|
|||||||
gem 'stripe', '5.7.1'
|
gem 'stripe', '5.7.1'
|
||||||
|
|
||||||
register_asset "stylesheets/common/discourse-patrons.scss"
|
register_asset "stylesheets/common/discourse-patrons.scss"
|
||||||
|
register_asset "stylesheets/common/discourse-patrons-layout.scss"
|
||||||
register_asset "stylesheets/mobile/discourse-patrons.scss"
|
register_asset "stylesheets/mobile/discourse-patrons.scss"
|
||||||
register_svg_icon "credit-card" if respond_to?(:register_svg_icon)
|
register_svg_icon "credit-card" if respond_to?(:register_svg_icon)
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ after_initialize do
|
|||||||
"../app/controllers/admin/products_controller",
|
"../app/controllers/admin/products_controller",
|
||||||
"../app/controllers/admin/subscriptions_controller",
|
"../app/controllers/admin/subscriptions_controller",
|
||||||
"../app/controllers/customers_controller",
|
"../app/controllers/customers_controller",
|
||||||
|
"../app/controllers/invoices_controller",
|
||||||
"../app/controllers/patrons_controller",
|
"../app/controllers/patrons_controller",
|
||||||
"../app/controllers/plans_controller",
|
"../app/controllers/plans_controller",
|
||||||
"../app/controllers/products_controller",
|
"../app/controllers/products_controller",
|
||||||
|
54
spec/requests/invoices_controller_spec.rb
Normal file
54
spec/requests/invoices_controller_spec.rb
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
module DiscoursePatrons
|
||||||
|
RSpec.describe InvoicesController do
|
||||||
|
describe "index" do
|
||||||
|
describe "not authenticated" do
|
||||||
|
it "does not list the invoices" do
|
||||||
|
::Stripe::Invoice.expects(:list).never
|
||||||
|
get "/patrons/invoices.json"
|
||||||
|
expect(response.status).to eq 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "authenticated" do
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:stripe_customer) { { id: 'cus_id4567' } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "other user invoices" do
|
||||||
|
it "does not list the invoices" do
|
||||||
|
::Stripe::Invoice.expects(:list).never
|
||||||
|
get "/patrons/invoices.json", params: { user_id: 999999 }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "own invoices" do
|
||||||
|
context "stripe customer does not exist" do
|
||||||
|
it "lists empty" do
|
||||||
|
::Stripe::Invoice.expects(:list).never
|
||||||
|
get "/patrons/invoices.json", params: { user_id: user.id }
|
||||||
|
expect(response.body).to eq "[]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "stripe customer exists" do
|
||||||
|
before do
|
||||||
|
DiscoursePatrons::Customer.create_customer(user, stripe_customer)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "lists the invoices" do
|
||||||
|
::Stripe::Invoice.expects(:list).with(customer: 'cus_id4567')
|
||||||
|
get "/patrons/invoices.json", params: { user_id: user.id }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user