diff --git a/Gemfile b/Gemfile index eb1ac0c..78a648b 100644 --- a/Gemfile +++ b/Gemfile @@ -9,14 +9,15 @@ gem 'bootsnap', '>= 1.4.2', require: false gem 'rack-cors', require: 'rack/cors' gem 'devise_token_auth' gem 'active_model_serializers', '~> 0.10.0' +gem 'rest-client' group :development, :test do - gem 'pry-rails' gem 'pry-byebug' gem 'shoulda-matchers' gem 'factory_bot_rails' gem 'rspec-rails' gem 'coveralls', require: false + gem 'webmock' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 93f8d0c..1ac15ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,6 +61,8 @@ GEM minitest (~> 5.1) tzinfo (~> 1.1) zeitwerk (~> 2.2) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) bcrypt (3.1.13) bootsnap (1.4.6) msgpack (~> 1.0) @@ -76,6 +78,8 @@ GEM term-ansicolor (~> 1.3) thor (>= 0.19.4, < 2.0) tins (~> 1.6) + crack (0.4.3) + safe_yaml (~> 1.0.0) crass (1.0.6) devise (4.7.1) bcrypt (~> 3.0) @@ -89,6 +93,8 @@ GEM rails (>= 4.2.0, < 6.1) diff-lcs (1.3) docile (1.3.2) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) erubi (1.9.0) factory_bot (5.1.1) activesupport (>= 4.2.0) @@ -98,6 +104,10 @@ GEM ffi (1.12.2) globalid (0.4.2) activesupport (>= 4.2.0) + hashdiff (1.0.1) + http-accept (1.7.0) + http-cookie (1.0.3) + domain_name (~> 0.5) i18n (1.8.2) concurrent-ruby (~> 1.0) json (2.3.0) @@ -114,11 +124,15 @@ GEM marcel (0.3.3) mimemagic (~> 0.3.2) method_source (1.0.0) + mime-types (3.3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2019.1009) mimemagic (0.3.4) mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.0) msgpack (1.3.3) + netrc (0.11.0) nio4r (2.5.2) nokogiri (1.10.9) mini_portile2 (~> 2.4.0) @@ -130,8 +144,7 @@ GEM pry-byebug (3.9.0) byebug (~> 11.0) pry (~> 0.13.0) - pry-rails (0.3.9) - pry (>= 0.10.4) + public_suffix (4.0.4) puma (4.3.3) nio4r (~> 2.0) rack (2.2.2) @@ -172,6 +185,11 @@ GEM responders (3.0.0) actionpack (>= 5.0) railties (>= 5.0) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) rspec-core (3.9.1) rspec-support (~> 3.9.1) rspec-expectations (3.9.1) @@ -190,6 +208,7 @@ GEM rspec-support (~> 3.9) rspec-support (3.9.2) ruby_dep (1.5.0) + safe_yaml (1.0.5) shoulda-matchers (4.3.0) activesupport (>= 4.2.0) simplecov (0.16.1) @@ -217,8 +236,15 @@ GEM sync tzinfo (1.2.7) thread_safe (~> 0.1) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.7) warden (1.2.8) rack (>= 2.0.6) + webmock (3.8.3) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) websocket-driver (0.7.1) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.4) @@ -236,14 +262,15 @@ DEPENDENCIES listen (>= 3.0.5, < 3.2) pg (>= 0.18, < 2.0) pry-byebug - pry-rails puma (~> 4.1) rack-cors rails (~> 6.0.2, >= 6.0.2.2) + rest-client rspec-rails shoulda-matchers spring spring-watcher-listen (~> 2.0.0) + webmock RUBY VERSION ruby 2.6.3p62 diff --git a/Swish_Merchant_TestCertificate_1231181189.p12 b/Swish_Merchant_TestCertificate_1231181189.p12 new file mode 100644 index 0000000..65c3650 Binary files /dev/null and b/Swish_Merchant_TestCertificate_1231181189.p12 differ diff --git a/Swish_TLS_RootCA.pem b/Swish_TLS_RootCA.pem new file mode 100644 index 0000000..8f3aea9 --- /dev/null +++ b/Swish_TLS_RootCA.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 027d728..6211e54 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -26,4 +26,4 @@ def find_profile def update_profile(attribute) @profile.update("#{attribute}": params['profile'][attribute]) end -end +end \ No newline at end of file diff --git a/app/controllers/swish_controller.rb b/app/controllers/swish_controller.rb new file mode 100644 index 0000000..c4f3ccf --- /dev/null +++ b/app/controllers/swish_controller.rb @@ -0,0 +1,60 @@ +class SwishController < ApplicationController + before_action :find_pong + + def create + + payload = { + callbackUrl: 'https://311f7df2.ngrok.io/swish/callback', + payeeAlias: ping_phone, + payerAlias: pong_phone, + amount: amount, + currency: 'SEK' + } + response = swish_call(:post, 'https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests/', payload) + body = ping_swish(response.headers[:location]) + render json: body + end + + private + + def find_pong + @swish_pong = Pong.find(params['swish']['pong_id']) + end + + def pong_phone + @swish_pong.user.phone_number + end + + def ping_phone + @swish_pong.ping.user.phone_number + end + + def amount + @swish_pong.total_cost.to_i + end + + def ping_swish(order) + sleep 4 + response = swish_call(:get, order) + body = JSON.parse(response.body) + body['status'] != 'CREATED' ? body : ping_swish(order) + end + + def swish_call(method, url, payload = {}) + p12 = OpenSSL::PKCS12.new(File.read("Swish_Merchant_TestCertificate_1231181189.p12"), "swish") + cert_store = OpenSSL::X509::Store.new + p12.ca_certs.each do | cert | + cert_store.add_cert(cert) + end + RestClient::Request.execute({ + method: method, + url: url, + payload: payload.to_json, + ssl_client_cert: p12.certificate, + ssl_client_key: p12.key, + ssl_cert_store: cert_store, + ssl_ca_file: 'Swish_TLS_RootCA.pem', + headers: { content_type: "application/json" } + }) + end +end diff --git a/config/routes.rb b/config/routes.rb index 798a665..8a15008 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,6 +7,7 @@ resources :pongs, only: %i[create update destroy show] resources :communities, only: [:index] resources :profiles, only: %i[show update] + resources :swish, only: %i[create] namespace :admin do resources :communities, only: %i[index update] end diff --git a/ngrok b/ngrok new file mode 100755 index 0000000..a237125 Binary files /dev/null and b/ngrok differ diff --git a/spec/fixtures/files/swish_response.json b/spec/fixtures/files/swish_response.json new file mode 100644 index 0000000..ab9c3e1 --- /dev/null +++ b/spec/fixtures/files/swish_response.json @@ -0,0 +1,16 @@ +{ + "id": "49DD3539A9394CC1A2E49A4D49B4AA23", + "payeePaymentReference": null, + "paymentReference": "F3403EA964AA4B5A8E67B5274ED5F289", + "callbackUrl": "https://311f7df2.ngrok.io/swish/callback", + "payerAlias": "1111111111", + "payeeAlias": "1231181189", + "amount": 54.0, + "currency": "SEK", + "message": "", + "status": "PAID", + "dateCreated": "2020-04-22T19:15:08.296Z", + "datePaid": "2020-04-22T19:15:12.296Z", + "errorCode": null, + "errorMessage": null +} diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 9c06596..e1876ad 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,8 +1,14 @@ require 'coveralls' Coveralls.wear_merged!('rails') - ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) +require 'webmock/rspec' +WebMock.enable! +# WebMock.disable! +WebMock.allow_net_connect! +include WebMock::API + +WebMock::API if Rails.env.production? abort('The Rails environment is running in production mode!') @@ -22,5 +28,41 @@ config.include FactoryBot::Syntax::Methods config.include(Shoulda::Matchers::ActiveRecord, type: :model) config.include ResponseJSON + # config.before do + # stub_request( + # :post, + # 'https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests/' + # ).with( + # body: + # "{\"callbackUrl\":\"https://311f7df2.ngrok.io/swish/callback\",\"payeeAlias\":\"1231181189\",\"payerAlias\":\"1111111111\",\"amount\":54,\"currency\":\"SEK\"}", + # headers: { + # 'Accept' => '*/*', + # 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + # 'Content-Length' => '139', + # 'Content-Type' => 'application/json', + # 'Host' => 'mss.cpc.getswish.net', + # 'User-Agent' => 'rest-client/2.1.0 (linux-gnu x86_64) ruby/2.6.3p62' + # } + # ).to_return( + # status: 200, body: file_fixture('swish_response.json'), headers: {} + # ) + # stub_request( + # :get, + # "https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests/A750387C14284222BD6F691BB4CE275A" + # ).with( + # # body: + # # "{\"callbackUrl\":\"https://311f7df2.ngrok.io/swish/callback\",\"payeeAlias\":\"1231181189\",\"payerAlias\":\"1111111111\",\"amount\":54,\"currency\":\"SEK\"}", + # headers: { + # 'Accept' => '*/*', + # 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + # 'Content-Length' => '139', + # 'Content-Type' => 'application/json', + # 'Host' => 'mss.cpc.getswish.net', + # 'User-Agent' => 'rest-client/2.1.0 (linux-gnu x86_64) ruby/2.6.3p62' + # } + # ).to_return( + # status: 200, body: file_fixture('swish_response.json'), headers: {} + # ) + # end end FactoryBot::SyntaxRunner.class_eval { include ActionDispatch::TestProcess } diff --git a/spec/requests/swish_request_spec.rb b/spec/requests/swish_request_spec.rb new file mode 100644 index 0000000..817ac7a --- /dev/null +++ b/spec/requests/swish_request_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +RSpec.describe 'Swishes', type: :request do + let!(:user) { create(:user, phone_number: '1111111111', name: 'pungrattan') } + let(:user_credentials) { user.create_new_auth_token } + let(:user_headers) do + { HTTP_ACCEPT: 'application/json' }.merge!(user_credentials) + end + let!(:user2) { create(:user, name: 'possum', phone_number: '1231181189') } + let!(:ping) { create(:ping, user_id: user2.id) } + let!(:pong) { create(:pong, user_id: user.id, total_cost: '54 kr', status: 'accepted', ping_id: ping.id) } + + before do + post '/swish', + params: { + swish: { + total_cost: pong.total_cost, + pong_id: pong.id + } + }, + headers: user_headers + end + + it 'returns a 200 response status' do + + expect(response).to have_http_status 200 + end + + xit 'returns payment status' do + expect(response_json['body']).to eq 'PAYMENT SUCCESSFUL' + end +end diff --git a/spec/requests/user/registration_spec.rb b/spec/requests/user/registration_spec.rb index 061e14f..b28b368 100644 --- a/spec/requests/user/registration_spec.rb +++ b/spec/requests/user/registration_spec.rb @@ -26,6 +26,11 @@ it 'returns a success message' do expect(response_json['status']).to eq 'success' end + + it 'assign user to correct community' do + user= User.last + expect(user.community).to eq community + end end context 'when a user submits' do