summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessandro Desantis <desa.alessandro@gmail.com>2021-03-28 10:53:13 +0200
committerGitHub <noreply@github.com>2021-03-28 10:53:13 +0200
commit73a6ddd9c21b089d6976c083b7bc22348d22c3dd (patch)
tree2c9c15039226e7a3c1f86c7b035d379886ab281f
parent6cbd8887c4df74e4e3a010c34b706156b16e8211 (diff)
parentc3154c77bf904526d778953b9ee235ac9c62e4ef (diff)
Merge pull request #199 from solidusio-contrib/mr/create-subscription-api
Setup the subcription create endpoint
-rw-r--r--app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb42
-rw-r--r--config/locales/en.yml1
-rw-r--r--config/routes.rb2
-rw-r--r--spec/jobs/solidus_subscriptions/process_installment_job_spec.rb17
-rw-r--r--spec/requests/api/v1/subscriptions_spec.rb98
5 files changed, 157 insertions, 3 deletions
diff --git a/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb b/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb
index 4cd698e..0ac694d 100644
--- a/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb
+++ b/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb
@@ -6,6 +6,26 @@ module SolidusSubscriptions
class SubscriptionsController < BaseController
protect_from_forgery unless: -> { request.format.json? }
+ def create
+ store = params[:store_id].nil? ? ::Spree::Store.default : ::Spree::Store.find(id: params[:store_id])
+ attributes = create_subscription_params.merge(user: current_api_user, store: store)
+ acceptable_payment_details = update_payment_attributes(attributes)
+
+ if acceptable_payment_details
+ subscription = SolidusSubscriptions::Subscription.new(attributes)
+
+ if subscription.save
+ render json: subscription.to_json(include: [:line_items, :shipping_address, :billing_address])
+ else
+ render json: subscription.errors.to_json, status: :unprocessable_entity
+ end
+ else
+ error_message = I18n.t('solidus_subscriptions.subscription.invalid_payment_details')
+
+ render json: { payment_source_type: [error_message] }.to_json, status: :unprocessable_entity
+ end
+ end
+
def update
load_subscription
@@ -43,6 +63,14 @@ module SolidusSubscriptions
authorize! action_name.to_sym, @subscription, subscription_guest_token
end
+ def create_subscription_params
+ params.require(:subscription).permit(
+ %i[payment_method_id payment_source_id shipping_address_id billing_address_id] |
+ SolidusSubscriptions.configuration.subscription_attributes |
+ [line_items_attributes: line_item_attributes]
+ )
+ end
+
def subscription_params
params.require(:subscription).permit(SolidusSubscriptions.configuration.subscription_attributes | [
line_items_attributes: line_item_attributes,
@@ -52,6 +80,20 @@ module SolidusSubscriptions
def line_item_attributes
SolidusSubscriptions.configuration.subscription_line_item_attributes - [:subscribable_id] + [:id]
end
+
+ def update_payment_attributes(attributes)
+ return true if attributes[:payment_method_id].blank? && attributes[:payment_source_id].blank?
+
+ payment_method = ::Spree::PaymentMethod.find_by(id: attributes[:payment_method_id])
+ payment_source = payment_method&.payment_source_class&.find_by(id: attributes[:payment_source_id])
+ if payment_method && payment_source
+ attributes[:payment_method] = payment_method
+ attributes[:payment_source] = payment_source
+ true
+ else
+ false
+ end
+ end
end
end
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 79d400a..9f26f8b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -8,6 +8,7 @@ en:
solidus_subscriptions:
subscription:
actionable_date: "Actionable Date"
+ invalid_payment_details: Invalid payment method or source
installment_details:
out_of_stock: >
This installment could not be processed because of insufficient
diff --git a/config/routes.rb b/config/routes.rb
index 04bad4c..cbed8c3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -4,7 +4,7 @@ SolidusSubscriptions::Engine.routes.draw do
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :line_items, only: [:update, :destroy]
- resources :subscriptions, only: [:update] do
+ resources :subscriptions, only: [:create, :update] do
member do
post :cancel
post :skip
diff --git a/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb b/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb
index 658e252..bc33d0e 100644
--- a/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb
+++ b/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb
@@ -13,13 +13,26 @@ RSpec.describe SolidusSubscriptions::ProcessInstallmentJob do
context 'when handling #perform errors' do
it 'swallows error when a proc is not configured' do
- expect { described_class.perform_now(nil) }.not_to raise_error(StandardError)
+ checkout = instance_double(SolidusSubscriptions::Checkout).tap do |c|
+ allow(c).to receive(:process).and_raise('test error')
+ end
+ allow(SolidusSubscriptions::Checkout).to receive(:new).and_return(checkout)
+
+ expect {
+ described_class.perform_now(build_stubbed(:installment))
+ }.not_to raise_error
end
it 'runs proc when a proc is configured' do
stub_config(processing_error_handler: proc { |e| raise e } )
+ checkout = instance_double(SolidusSubscriptions::Checkout).tap do |c|
+ allow(c).to receive(:process).and_raise('test error')
+ end
+ allow(SolidusSubscriptions::Checkout).to receive(:new).and_return(checkout)
- expect { described_class.perform_now(nil) }.to raise_error(StandardError)
+ expect {
+ described_class.perform_now(build_stubbed(:installment))
+ }.to raise_error(/test error/)
end
end
end
diff --git a/spec/requests/api/v1/subscriptions_spec.rb b/spec/requests/api/v1/subscriptions_spec.rb
index 6807c67..02fa6d5 100644
--- a/spec/requests/api/v1/subscriptions_spec.rb
+++ b/spec/requests/api/v1/subscriptions_spec.rb
@@ -3,6 +3,104 @@
RSpec.describe '/api/v1/subscriptions' do
include SolidusSubscriptions::Engine.routes.url_helpers
+ describe 'POST /' do
+ context 'with valid params' do
+ it 'creates the subscription and responds with 200 OK' do
+ user = create(:user, &:generate_spree_api_key!)
+
+ expect do
+ post(
+ api_v1_subscriptions_path,
+ params: { subscription: { interval_length: 11 } },
+ headers: { 'Authorization' => "Bearer #{user.spree_api_key}" },
+ )
+ end.to change(SolidusSubscriptions::Subscription, :count).from(0).to(1)
+
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'with invalid params' do
+ it "doesn't create the subscription and responds with 422 Unprocessable Entity" do
+ user = create(:user, &:generate_spree_api_key!)
+
+ expect do
+ post(
+ api_v1_subscriptions_path,
+ params: { subscription: { interval_length: -1 } },
+ headers: { 'Authorization' => "Bearer #{user.spree_api_key}" },
+ )
+ end.not_to change(SolidusSubscriptions::Subscription, :count)
+
+ expect(response.status).to eq(422)
+ end
+ end
+
+ context 'when valid payment attributes are provided' do
+ # rubocop:disable RSpec/MultipleExpectations
+ it 'creates the subscription using the specified payment' do
+ user = create(:user, &:generate_spree_api_key!)
+ payment_source = create(:credit_card, user: user)
+ payment_params = { payment_method_id: payment_source.payment_method.id, payment_source_id: payment_source.id }
+
+ expect(user.wallet.default_wallet_payment_source).to be_nil
+ expect do
+ post(
+ api_v1_subscriptions_path,
+ params: { subscription: { interval_length: 7 }.merge(payment_params) },
+ headers: { 'Authorization' => "Bearer #{user.spree_api_key}" },
+ )
+ end.to change(SolidusSubscriptions::Subscription, :count).from(0).to(1)
+ expect(SolidusSubscriptions::Subscription.last).to have_attributes(payment_params)
+ end
+ # rubocop:enable RSpec/MultipleExpectations
+ end
+
+ # rubocop:disable RSpec/MultipleExpectations
+ context 'when an invalid payment method is provided' do
+ it "doesn't create the subscription and responds with 422 Unprocessable Entity" do
+ user = create(:user, &:generate_spree_api_key!)
+ payment_source = create(:credit_card)
+ payment_params = { payment_source_id: payment_source.id }
+
+ expect do
+ post(
+ api_v1_subscriptions_path,
+ params: { subscription: { interval_length: 7 }.merge(payment_params) },
+ headers: { 'Authorization' => "Bearer #{user.spree_api_key}" },
+ )
+ end.not_to change(SolidusSubscriptions::Subscription, :count)
+
+ error_message = I18n.t('solidus_subscriptions.subscription.invalid_payment_details')
+ response_body = JSON.parse(response.body)
+ expect(response_body).to eq('payment_source_type' => [error_message])
+ expect(response.status).to eq(422)
+ end
+ end
+
+ context 'when an invalid payment source is provided' do
+ it "doesn't create the subscription and responds with 422 Unprocessable Entity" do
+ user = create(:user, &:generate_spree_api_key!)
+ payment_source = create(:credit_card)
+ payment_params = { payment_method_id: payment_source.payment_method.id }
+
+ expect do
+ post(
+ api_v1_subscriptions_path,
+ params: { subscription: { interval_length: 7 }.merge(payment_params) },
+ headers: { 'Authorization' => "Bearer #{user.spree_api_key}" },
+ )
+ end.not_to change(SolidusSubscriptions::Subscription, :count)
+
+ error_message = I18n.t('solidus_subscriptions.subscription.invalid_payment_details')
+ response_body = JSON.parse(response.body)
+ expect(response_body).to eq('payment_source_type' => [error_message])
+ expect(response.status).to eq(422)
+ end
+ end
+ # rubocop:enable RSpec/MultipleExpectations
+ end
+
describe 'PATCH /:id' do
context 'when the subscription belongs to the user' do
context 'with valid params' do