├── spec
├── test_app
│ ├── log
│ │ └── .keep
│ ├── lib
│ │ └── assets
│ │ │ └── .keep
│ ├── public
│ │ ├── favicon.ico
│ │ ├── apple-touch-icon.png
│ │ ├── apple-touch-icon-precomposed.png
│ │ ├── 500.html
│ │ ├── 422.html
│ │ └── 404.html
│ ├── app
│ │ ├── controllers
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ ├── application_controller.rb
│ │ │ └── pages_controller.rb
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ └── views
│ │ │ ├── pages
│ │ │ ├── done.html.erb
│ │ │ └── credit_card_payment_button.html.erb
│ │ │ └── layouts
│ │ │ └── application.html.erb
│ ├── package.json
│ ├── bin
│ │ ├── rake
│ │ ├── bundle
│ │ ├── rails
│ │ ├── yarn
│ │ ├── update
│ │ └── setup
│ ├── config
│ │ ├── spring.rb
│ │ ├── environment.rb
│ │ ├── initializers
│ │ │ ├── mime_types.rb
│ │ │ ├── application_controller_renderer.rb
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── cookies_serializer.rb
│ │ │ ├── wrap_parameters.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── assets.rb
│ │ │ ├── inflections.rb
│ │ │ └── spgateway.rb
│ │ ├── cable.yml
│ │ ├── routes.rb
│ │ ├── boot.rb
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── application.rb
│ │ ├── secrets.yml
│ │ ├── environments
│ │ │ ├── test.rb
│ │ │ ├── development.rb
│ │ │ └── production.rb
│ │ └── puma.rb
│ ├── config.ru
│ └── Rakefile
├── lib
│ ├── spgateway_spec.rb
│ └── spgateway
│ │ ├── sha256_spec.rb
│ │ └── mpg_form_spec.rb
├── integration_spec_helper.rb
├── support
│ └── wait_for.rb
├── features
│ └── credit_card_payment_spec.rb
├── rails_helper.rb
└── spec_helper.rb
├── .rspec
├── lib
├── spgateway
│ ├── version.rb
│ ├── rails.rb
│ ├── rails
│ │ └── engine.rb
│ ├── attr_key_helper.rb
│ ├── engine.rb
│ ├── sha256.rb
│ ├── config.rb
│ ├── response.rb
│ └── mpg_form.rb
├── tasks
│ └── spgateway
│ │ └── rails_tasks.rake
├── spgateway.rb
└── generators
│ └── spgateway
│ ├── install_generator.rb
│ └── templates
│ └── spgateway_initializer.rb
├── Appraisals
├── config
├── initializers
│ └── inflections.rb
└── routes.rb
├── gemfiles
├── rails_4.gemfile
├── rails_5.gemfile
├── rails_4.gemfile.lock
└── rails_5.gemfile.lock
├── .gitignore
├── app
├── jobs
│ └── spgateway
│ │ └── rails
│ │ └── application_job.rb
├── controllers
│ └── spgateway
│ │ ├── notify_callbacks_controller.rb
│ │ ├── mpg_callbacks_controller.rb
│ │ ├── payment_code_callbacks_controller.rb
│ │ └── application_controller.rb
└── helpers
│ └── spgateway
│ └── application_helper.rb
├── .rubocop.yml
├── Rakefile
├── Gemfile
├── .travis.yml
├── bin
└── rails
├── spgateway-rails.gemspec
├── MIT-LICENSE
├── .rubocop_todo.yml
├── Gemfile.lock
└── README.md
/spec/test_app/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/test_app/lib/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/test_app/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/test_app/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/test_app/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/test_app/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --format documentation
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/spec/test_app/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/lib/spgateway/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | VERSION = '0.1.0'
4 | end
5 |
--------------------------------------------------------------------------------
/spec/test_app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test_app",
3 | "private": true,
4 | "dependencies": {}
5 | }
6 |
--------------------------------------------------------------------------------
/spec/test_app/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise "rails-4" do
2 | gem "rails", "4.2"
3 | end
4 |
5 | appraise "rails-5" do
6 | gem "rails", "5.1"
7 | end
8 |
--------------------------------------------------------------------------------
/lib/spgateway/rails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spgateway'
3 |
4 | module Spgateway
5 | module Rails
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | ActiveSupport::Inflector.inflections(:en) do |inflect|
3 | inflect.acronym 'MPG'
4 | end
5 |
--------------------------------------------------------------------------------
/gemfiles/rails_4.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rails", "4.2"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/rails_5.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rails", "5.1"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/spec/test_app/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery with: :exception
3 | end
4 |
--------------------------------------------------------------------------------
/spec/test_app/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/spec/test_app/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w(
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ).each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/spec/test_app/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | log/*.log
3 | pkg/
4 | spec/test_app/db/*.sqlite3
5 | spec/test_app/db/*.sqlite3-journal
6 | spec/test_app/log/*.log
7 | spec/test_app/tmp/
8 | coverage/*
9 |
--------------------------------------------------------------------------------
/lib/tasks/spgateway/rails_tasks.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # desc "Explaining what the task does"
3 | # task :spgateway_rails do
4 | # # Task goes here
5 | # end
6 |
--------------------------------------------------------------------------------
/spec/test_app/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/spec/test_app/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/app/jobs/spgateway/rails/application_job.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | module Rails
4 | class ApplicationJob < ActiveJob::Base
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/test_app/app/views/pages/done.html.erb:
--------------------------------------------------------------------------------
1 |
DONE
2 |
3 | <% if @data %>
4 |
5 | Data:
6 |
7 |
8 | <%= @data %>
9 |
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/lib/spgateway/rails/engine.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spgateway/engine'
3 |
4 | module Spgateway
5 | module Rails
6 | class Engine < Spgateway::Engine
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/spec/test_app/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: redis://localhost:6379/1
10 | channel_prefix: test_app_production
11 |
--------------------------------------------------------------------------------
/spec/test_app/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | mount Spgateway::Engine => '/spgateway'
3 |
4 | get 'pages/credit_card_payment_button', to: 'pages#credit_card_payment_button'
5 | get 'pages/done', to: 'pages#done'
6 | end
7 |
--------------------------------------------------------------------------------
/spec/test_app/app/views/pages/credit_card_payment_button.html.erb:
--------------------------------------------------------------------------------
1 | <%= spgateway_pay_button 'Go pay', payment_methods: [:credit_card], order_number: SecureRandom.hex(10), item_description: 'Test order', amount: '1000', payer_email: 'test@example.com' %>
2 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ApplicationController.renderer.defaults.merge!(
4 | # http_host: 'example.org',
5 | # https: false
6 | # )
7 |
--------------------------------------------------------------------------------
/lib/spgateway/attr_key_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | module AttrKeyHelper
4 | def convert_to_attr_key(term)
5 | term.to_s.gsub(/(^|_)(url|id|[a-z0-9])/) { Regexp.last_match[2].upcase }
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/test_app/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/spec/test_app/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
3 |
4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
6 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | Spgateway::Engine.routes.draw do
3 | post 'mpg_callbacks', to: 'mpg_callbacks#proceed'
4 | post 'payment_code_callbacks', to: 'payment_code_callbacks#proceed'
5 | post 'notify_callbacks', to: 'notify_callbacks#proceed'
6 | end
7 |
--------------------------------------------------------------------------------
/lib/spgateway/engine.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | class Engine < ::Rails::Engine
4 | isolate_namespace Spgateway
5 |
6 | config.to_prepare do
7 | ::ApplicationController.helper(Spgateway::Engine.helpers)
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/test_app/app/controllers/pages_controller.rb:
--------------------------------------------------------------------------------
1 | class PagesController < ApplicationController
2 | def credit_card_payment_button
3 | end
4 |
5 | def done
6 | if params[:data]
7 | @data = JSON.pretty_generate(JSON.parse(params[:data]))
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/lib/spgateway_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spec_helper'
3 | require 'spgateway/version'
4 |
5 | RSpec.describe Spgateway do
6 | describe '::VERSION' do
7 | it 'is a string' do
8 | expect(Spgateway::VERSION).to be_a_kind_of(String)
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | DisplayCopNames: true
3 | TargetRubyVersion: 2.4
4 | Include:
5 | - Rakefile
6 | - lib/**/*.rake
7 | Exclude:
8 | - Gemfile
9 | - Appraisals
10 | - spgateway-rails.gemspec
11 | - lib/generators/**/*
12 | - spec/test_app/**/*
13 | Rails:
14 | Enabled: true
15 | inherit_from: .rubocop_todo.yml
16 |
--------------------------------------------------------------------------------
/spec/test_app/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | VENDOR_PATH = File.expand_path('..', __dir__)
3 | Dir.chdir(VENDOR_PATH) do
4 | begin
5 | exec "yarnpkg #{ARGV.join(" ")}"
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/test_app/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TestApp
5 | <%= csrf_meta_tags %>
6 |
7 |
8 |
9 | <% if flash.any? %>
10 | <% flash.each do |type, message| %>
11 | <%= "#{type}: #{message}" %>
12 | <% end %>
13 | <% end %>
14 |
15 | <%= yield %>
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/spgateway.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spgateway/version'
3 | require 'spgateway/config'
4 | require 'spgateway/engine'
5 | require 'spgateway/sha256'
6 | require 'spgateway/response'
7 | require 'spgateway/mpg_form'
8 |
9 | module Spgateway
10 | def self.configure
11 | yield config
12 | end
13 |
14 | def self.config
15 | @config ||= Config.new
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/lib/spgateway/sha256.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | module SHA256
4 | def self.hash(
5 | data,
6 | hash_key: Spgateway.config.hash_key,
7 | hash_iv: Spgateway.config.hash_iv,
8 | hash_iv_first: false
9 | )
10 | if hash_iv_first
11 | Digest::SHA256.hexdigest("HashIV=#{hash_iv}{data}&HashKey=#{hash_key}").upcase
12 | else
13 | Digest::SHA256.hexdigest("HashKey=#{hash_key}{data}&HashIV=#{hash_iv}").upcase
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | begin
3 | require 'bundler/setup'
4 | rescue LoadError
5 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6 | end
7 |
8 | require 'rdoc/task'
9 |
10 | RDoc::Task.new(:rdoc) do |rdoc|
11 | rdoc.rdoc_dir = 'rdoc'
12 | rdoc.title = 'Spgateway::Rails'
13 | rdoc.options << '--line-numbers'
14 | rdoc.rdoc_files.include('README.md')
15 | rdoc.rdoc_files.include('lib/**/*.rb')
16 | end
17 |
18 | load 'rails/tasks/statistics.rake'
19 |
20 | require 'bundler/gem_tasks'
21 |
--------------------------------------------------------------------------------
/app/controllers/spgateway/notify_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require_dependency 'spgateway/application_controller'
3 |
4 | module Spgateway
5 | class NotifyCallbacksController < ApplicationController
6 | def proceed
7 | raise NotImplementedError, 'Spgateway.config.notify_callback is not a proc.' unless Spgateway.config.notify_callback.is_a? Proc
8 | instance_exec(spgateway_response, self, ::Rails.application.routes.url_helpers, &Spgateway.config.notify_callback)
9 | head 200
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/integration_spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | ENV['RAILS_ENV'] ||= 'test'
3 | require File.expand_path('../test_app/config/environment', __FILE__)
4 | abort('The Rails environment is running in production mode!') if Rails.env.production?
5 | require 'rails_helper'
6 | require 'capybara/rspec'
7 | require 'capybara/poltergeist'
8 | require 'ngrok/rspec'
9 | Capybara.server_port = 3003
10 | Capybara.javascript_driver = :poltergeist
11 | Ngrok::Rspec.tunnel = { port: Capybara.server_port }
12 | RSpec.configure { |config| config.include Ngrok::Rspec }
13 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Declare your gem's dependencies in spgateway-rails.gemspec.
4 | # Bundler will treat runtime dependencies like base dependencies, and
5 | # development dependencies will be added by default to the :development group.
6 | gemspec
7 |
8 | # Declare any dependencies that are still in development here instead of in
9 | # your gemspec. These might include edge Rails or gems from your path or
10 | # Git. Remember to move these dependencies to your gemspec before releasing
11 | # your gem to rubygems.org.
12 |
13 | # To use a debugger
14 | # gem 'byebug', group: [:development, :test]
15 |
--------------------------------------------------------------------------------
/app/controllers/spgateway/mpg_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require_dependency 'spgateway/application_controller'
3 |
4 | module Spgateway
5 | class MPGCallbacksController < ApplicationController
6 | skip_before_action :verify_authenticity_token
7 |
8 | def proceed
9 | raise NotImplementedError, 'Spgateway.config.mpg_callback is not a proc.' unless Spgateway.config.mpg_callback.is_a? Proc
10 | instance_exec(spgateway_response, self, ::Rails.application.routes.url_helpers, &Spgateway.config.mpg_callback)
11 | redirect_to '/' unless performed?
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | cache: bundler
3 | rvm:
4 | - 2.2.0
5 | - 2.3.0
6 | - 2.4.0
7 | gemfile:
8 | - gemfiles/rails_4.gemfile
9 | - gemfiles/rails_5.gemfile
10 | matrix:
11 | exclude:
12 | - rvm: 2.2.0
13 | gemfile: gemfiles/rails_5.gemfile
14 | - rvm: 2.3.0
15 | gemfile: gemfiles/rails_5.gemfile
16 | - rvm: 2.4.0
17 | gemfile: gemfiles/rails_4.gemfile
18 | before_install:
19 | - "gem update bundler"
20 | - "mkdir -p ~/bin"
21 | - "cd ~/bin"
22 | - "curl https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip -o ngrok.zip"
23 | - "unzip ngrok.zip"
24 | - "cd -"
25 | script: "bundle exec rspec spec"
26 |
--------------------------------------------------------------------------------
/spec/support/wait_for.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module WaitFor
3 | def wait_for_ajax(max_wait_time: Capybara.default_max_wait_time)
4 | Timeout.timeout(max_wait_time) do
5 | loop until finished_all_ajax_requests?
6 | end
7 | end
8 |
9 | def wait_for_content(text, max_wait_time: Capybara.default_max_wait_time)
10 | Timeout.timeout(max_wait_time) do
11 | loop until page.has_content? text
12 | end
13 | end
14 |
15 | def finished_all_ajax_requests?
16 | page.evaluate_script('jQuery.active').zero?
17 | end
18 | end
19 |
20 | RSpec.configure do |config|
21 | config.include WaitFor, type: :feature
22 | end
23 |
--------------------------------------------------------------------------------
/app/controllers/spgateway/payment_code_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require_dependency 'spgateway/application_controller'
3 |
4 | module Spgateway
5 | class PaymentCodeCallbacksController < ApplicationController
6 | skip_before_action :verify_authenticity_token
7 |
8 | def proceed
9 | raise NotImplementedError, 'Spgateway.config.payment_code_callback is not a proc.' unless Spgateway.config.payment_code_callback.is_a? Proc
10 | instance_exec(spgateway_response, self, ::Rails.application.routes.url_helpers, &Spgateway.config.payment_code_callback)
11 | redirect_to '/' unless performed?
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/generators/spgateway/install_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'rails/generators/base'
3 |
4 | module Spgateway
5 | module Generators
6 | class InstallGenerator < ::Rails::Generators::Base
7 | source_root File.expand_path('../templates', __FILE__)
8 |
9 | desc 'Creates the Spgateway initializer and mounts Spgateway::Rails::Engine.'
10 |
11 | def copy_initializer_file
12 | template 'spgateway_initializer.rb', ::Rails.root.join('config', 'initializers', 'spgateway.rb')
13 | end
14 |
15 | def mount_engine
16 | route "mount Spgateway::Engine => '/spgateway'"
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 | # This command will automatically be run when you run "rails" with Rails gems
4 | # installed from the root of your application.
5 |
6 | ENGINE_ROOT = File.expand_path('../..', __FILE__)
7 | ENGINE_PATH = File.expand_path('../../lib/spgateway/rails/engine', __FILE__)
8 | APP_PATH = File.expand_path('../../spec/test_app/config/application', __FILE__)
9 |
10 | # Set up gems listed in the Gemfile.
11 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
12 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
13 |
14 | require 'action_controller/railtie'
15 | require 'action_mailer/railtie'
16 | require 'active_job/railtie'
17 |
18 | require 'rails/engine/commands'
19 |
--------------------------------------------------------------------------------
/spec/test_app/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a way to update your development environment automatically.
15 | # Add necessary update steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | puts "\n== Removing old logs and tempfiles =="
22 | system! 'bin/rails log:clear tmp:clear'
23 |
24 | puts "\n== Restarting application server =="
25 | system! 'bin/rails restart'
26 | end
27 |
--------------------------------------------------------------------------------
/spec/test_app/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a starting point to setup your application.
15 | # Add necessary setup steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | # Install JavaScript dependencies if using Yarn
22 | # system('bin/yarn')
23 |
24 |
25 | puts "\n== Removing old logs and tempfiles =="
26 | system! 'bin/rails log:clear tmp:clear'
27 |
28 | puts "\n== Restarting application server =="
29 | system! 'bin/rails restart'
30 | end
31 |
--------------------------------------------------------------------------------
/lib/spgateway/config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | class Config
4 | include ActiveSupport::Configurable
5 | config_accessor :merchant_id
6 | config_accessor :hash_iv, :hash_key
7 | config_accessor :api_url, :mpg_gateway_url
8 | config_accessor :mpg_callback, :notify_callback, :payment_code_callback
9 |
10 | def mpg_callback(&block)
11 | if block
12 | config.mpg_callback = block
13 | else
14 | config.mpg_callback
15 | end
16 | end
17 |
18 | def notify_callback(&block)
19 | if block
20 | config.notify_callback = block
21 | else
22 | config.notify_callback
23 | end
24 | end
25 |
26 | def payment_code_callback(&block)
27 | if block
28 | config.payment_code_callback = block
29 | else
30 | config.payment_code_callback
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/spgateway.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | Spgateway.configure do |config|
3 | config.merchant_id = 'MS11591073'
4 | config.hash_key = '7KZsaNdslfbViAsPIz2gaEw4CjIHwdXE'
5 | config.hash_iv = 'XX67KkA6zM2SmuH4'
6 |
7 | config.api_url = 'https://ccore.spgateway.com/API'
8 | config.mpg_gateway_url = 'https://ccore.spgateway.com/MPG/mpg_gateway'
9 |
10 | config.mpg_callback do |spgateway_response|
11 | # Pass the response into a mocked module so we can inspect it in the test
12 | MPGCallbackTesting.response(spgateway_response)
13 |
14 | flash[:notice] = "Payment has been proceeded."
15 |
16 | redirect_to pages_done_path(
17 | data: {
18 | spgateway_response: spgateway_response
19 | }.to_json
20 | )
21 | end
22 |
23 | config.notify_callback do |spgateway_response|
24 | # Pass the response into a mocked module so we can inspect it in the test
25 | NotifyCallbackTesting.response(spgateway_response)
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/test_app/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/spec/test_app/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | # Pick the frameworks you want:
4 | # require "active_record/railtie"
5 | require "action_controller/railtie"
6 | require "action_view/railtie"
7 | # require "action_mailer/railtie"
8 | require "active_job/railtie"
9 | # require "action_cable/engine"
10 | # require "rails/test_unit/railtie"
11 | require "sprockets/railtie"
12 |
13 | Bundler.require(*Rails.groups)
14 | require "spgateway/rails"
15 |
16 | module TestApp
17 | class Application < Rails::Application
18 | puts "Ruby Version: #{RUBY_VERSION}"
19 | puts "Rails Version: #{Rails::VERSION::STRING}"
20 |
21 | # Initialize configuration defaults for originally generated Rails version.
22 | # config.load_defaults 5.1
23 |
24 | # Settings in config/environments/* take precedence over those specified here.
25 | # Application configuration should go into files in config/initializers
26 | # -- all .rb files in that directory are automatically loaded.
27 | end
28 | end
29 |
30 |
--------------------------------------------------------------------------------
/app/controllers/spgateway/application_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | class ApplicationController < ActionController::Base
4 | protect_from_forgery with: :null_session
5 |
6 | private
7 |
8 | def spgateway_response
9 | return @spgateway_response if @spgateway_response
10 | return nil unless params[:JSONData]
11 | @spgateway_response = Spgateway::Response.new(params[:JSONData])
12 | raise InvalidResponseError unless @spgateway_response.result.valid?
13 | @spgateway_response
14 | end
15 |
16 | class InvalidResponseError < StandardError
17 | end
18 |
19 | def respond_to_missing?(method)
20 | super || ::Rails.application.routes.url_helpers.respond_to?(method)
21 | end
22 |
23 | def method_missing(method, *args)
24 | if ::Rails.application.routes.url_helpers.respond_to?(method)
25 | ::Rails.application.routes.url_helpers.try(method, *args)
26 | else
27 | super
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/lib/spgateway/sha256_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spec_helper'
3 | require 'spgateway/sha256'
4 |
5 | RSpec.describe Spgateway::SHA256 do
6 | describe '.hash' do
7 | it 'work as expected' do
8 | data = 'Amt=200&MerchantID=123456&MerchantOrderNo=20140901001&TimeStamp=1403243286&Version=1.1'
9 | hash_key = '1A3S21DAS3D1AS65D1'
10 | hash_iv = '1AS56D1AS24D'
11 | hash = Spgateway::SHA256.hash(data, hash_key: hash_key, hash_iv: hash_iv)
12 |
13 | expect(hash).to eq('841F57D750FB4B04B62DDC3ECDC26F1F4028410927DD28BD5B2E34791CC434D2')
14 | end
15 |
16 | it 'work as expected with hash_iv_first: true' do
17 | data = 'Amt=100&MerchantID=1422967&MerchantOrderNo=840f022&TradeNo=14061313541640927'
18 | hash_key = 'abcdefg'
19 | hash_iv = '1234567'
20 | hash = Spgateway::SHA256.hash(data, hash_key: hash_key, hash_iv: hash_iv, hash_iv_first: true)
21 |
22 | expect(hash).to eq('62C687AF6409E46E79769FAF54F54FE7E75AAE50BAF0767752A5C337670B8EDB')
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spgateway-rails.gemspec:
--------------------------------------------------------------------------------
1 | $:.push File.expand_path("../lib", __FILE__)
2 |
3 | # Maintain your gem's version:
4 | require "spgateway/version"
5 |
6 | # Describe your gem and declare its dependencies:
7 | Gem::Specification.new do |s|
8 | s.name = "spgateway-rails"
9 | s.version = Spgateway::VERSION
10 | s.authors = ["zetavg"]
11 | s.email = ["mail@zeta.vg"]
12 | # s.homepage = "TODO"
13 | s.summary = "API wrapper for www.spgateway.com."
14 | # s.description = "TODO: Description of Spgateway."
15 | s.license = "MIT"
16 |
17 | s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
18 |
19 | s.add_dependency "rails", ">= 4.0", "< 5.2"
20 |
21 | s.add_development_dependency "rspec-rails"
22 | s.add_development_dependency "capybara"
23 | s.add_development_dependency "poltergeist"
24 | s.add_development_dependency "appraisal"
25 | s.add_development_dependency "ngrok-rspec"
26 | s.add_development_dependency "simplecov"
27 | s.add_development_dependency "coveralls"
28 | end
29 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 zetavg
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/spec/lib/spgateway/mpg_form_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spec_helper'
3 | require 'spgateway/mpg_form'
4 |
5 | RSpec.describe Spgateway::MPGForm do
6 | describe 'initialize' do
7 | it 'raise an error if the parameter is not a Hash' do
8 | expect { Spgateway::MPGForm.new('hi') }.to raise_error(ArgumentError, /must pass a hash/)
9 | end
10 |
11 | it 'raise an error if required parameter is missing' do
12 | expect { Spgateway::MPGForm.new(MerchantID: 'abc', MerchantOrderNo: 1, ItemDesc: 2) }.to raise_error(ArgumentError, /'Amt' is missing/)
13 | end
14 | end
15 |
16 | describe '#*' do
17 | it 'sets or gets the attrs' do
18 | form = Spgateway::MPGForm.new(MerchantID: 'abc', MerchantOrderNo: 1, ItemDesc: 2, Amt: 123)
19 | form.Abc = 1
20 | expect(form.Abc).to eq(1)
21 | end
22 | end
23 |
24 | describe '#set_attr' do
25 | it 'sets the attr for the form' do
26 | form = Spgateway::MPGForm.new(MerchantID: 'abc', MerchantOrderNo: 1, ItemDesc: 2, Amt: 123)
27 | form.set_attr('Abc', 1)
28 | expect(form.Abc).to eq(1)
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/test_app/config/secrets.yml:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rails secret` to generate a secure secret key.
9 |
10 | # Make sure the secrets in this file are kept private
11 | # if you're sharing your code publicly.
12 |
13 | # Shared secrets are available across all environments.
14 |
15 | # shared:
16 | # api_key: a1B2c3D4e5F6
17 |
18 | # Environmental secrets are only available for that specific environment.
19 |
20 | development:
21 | secret_key_base: f3363516e3053311e9267b3c5501d1e751a2b1c2be34d2b07ebfaf072246b04bd9544bf7e52990f79ae54bdfb2337a096ba244b6fbc147572b0c70f7a639164d
22 |
23 | test:
24 | secret_key_base: 3a8eb88e83fd7479c3e92cc90d40d9d683e8655c86fcf73c477a6a26f87d9c30132de070a2caf1c32469cbad493f89f4bf8df4650c2c06a6b1ab43bb6ddd5dc6
25 |
26 | # Do not keep production secrets in the unencrypted secrets file.
27 | # Instead, either read values from the environment.
28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets
29 | # and move the `production:` environment over there.
30 |
31 | production:
32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
33 |
--------------------------------------------------------------------------------
/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 | # This configuration was generated by
2 | # `rubocop --auto-gen-config`
3 | # on 2017-05-12 13:23:20 +0800 using RuboCop version 0.46.0.
4 | # The point is for the user to remove these configuration records
5 | # one by one as the offenses are removed from the code base.
6 | # Note that changes in the inspected code, or installation of new
7 | # versions of RuboCop, may require this file to be generated again.
8 |
9 | # Offense count: 3
10 | Metrics/AbcSize:
11 | Max: 34
12 |
13 | # Offense count: 1
14 | Metrics/CyclomaticComplexity:
15 | Max: 8
16 |
17 | # Offense count: 20
18 | # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
19 | # URISchemes: http, https
20 | Metrics/LineLength:
21 | Max: 182
22 |
23 | # Offense count: 3
24 | # Configuration parameters: CountComments.
25 | Metrics/MethodLength:
26 | Max: 29
27 |
28 | # Offense count: 1
29 | Metrics/PerceivedComplexity:
30 | Max: 8
31 |
32 | # Offense count: 1
33 | # Cop supports --auto-correct.
34 | Style/BlockComments:
35 | Exclude:
36 | - 'spec/spec_helper.rb'
37 |
38 | # Offense count: 11
39 | Style/Documentation:
40 | Exclude:
41 | - 'spec/**/*'
42 | - 'test/**/*'
43 | - 'app/controllers/spgateway/application_controller.rb'
44 | - 'app/controllers/spgateway/mpg_callbacks_controller.rb'
45 | - 'app/helpers/spgateway/application_helper.rb'
46 | - 'lib/spgateway.rb'
47 | - 'lib/spgateway/config.rb'
48 | - 'lib/spgateway/engine.rb'
49 | - 'lib/spgateway/mpg_form.rb'
50 | - 'lib/spgateway/rails.rb'
51 | - 'lib/spgateway/response.rb'
52 | - 'lib/spgateway/sha256.rb'
53 |
--------------------------------------------------------------------------------
/spec/test_app/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | # config.public_file_server.enabled = true
17 | # config.public_file_server.headers = {
18 | # 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}"
19 | # }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Print deprecation notices to the stderr.
32 | config.active_support.deprecation = :stderr
33 |
34 | # Raises error for missing translations
35 | # config.action_view.raise_on_missing_translations = true
36 | end
37 |
--------------------------------------------------------------------------------
/spec/test_app/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | if Rails.root.join('tmp/caching-dev.txt').exist?
17 | config.action_controller.perform_caching = true
18 |
19 | config.cache_store = :memory_store
20 | config.public_file_server.headers = {
21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
22 | }
23 | else
24 | config.action_controller.perform_caching = false
25 |
26 | config.cache_store = :null_store
27 | end
28 |
29 | # Print deprecation notices to the Rails logger.
30 | config.active_support.deprecation = :log
31 |
32 | # Debug mode disables concatenation and preprocessing of assets.
33 | # This option may cause significant delays in view rendering with a large
34 | # number of complex assets.
35 | config.assets.debug = true
36 |
37 | # Suppress logger output for asset requests.
38 | config.assets.quiet = true
39 |
40 | # Raises error for missing translations
41 | # config.action_view.raise_on_missing_translations = true
42 |
43 | # Use an evented file watcher to asynchronously detect changes in source code,
44 | # routes, locales, etc. This feature depends on the listen gem.
45 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
46 | end
47 |
--------------------------------------------------------------------------------
/spec/test_app/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/spec/test_app/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/spec/test_app/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/lib/spgateway/response.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spgateway/attr_key_helper'
3 |
4 | module Spgateway
5 | class Response
6 | attr_reader :status
7 | attr_reader :message
8 | attr_reader :result
9 |
10 | def initialize(data)
11 | data = JSON.parse(data) if data.is_a? String
12 |
13 | @status = data['Status']
14 | @message = data['Message']
15 |
16 | result = data['Result']
17 |
18 | result = JSON.parse(result) if result.is_a? String
19 |
20 | @result = Result.new(result)
21 | end
22 |
23 | class Result
24 | include AttrKeyHelper
25 |
26 | def initialize(data)
27 | @data = data
28 | end
29 |
30 | def valid?
31 | try(:check_value) == expected_check_value ||
32 | try(:check_code) == expected_check_code
33 | end
34 |
35 | def expected_check_value
36 | data = "Amt=#{@data['Amt']}&MerchantID=#{@data['MerchantID']}&MerchantOrderNo=#{@data['MerchantOrderNo']}&TimeStamp=#{@data['TimeStamp']}&Version=#{@data['Version']}"
37 | Spgateway::SHA256.hash(data)
38 | end
39 |
40 | def expected_check_code
41 | data = "Amt=#{@data['Amt']}&MerchantID=#{@data['MerchantID']}&MerchantOrderNo=#{@data['MerchantOrderNo']}&TradeNo=#{@data['TradeNo']}"
42 | Spgateway::SHA256.hash(data, hash_iv_first: true)
43 | end
44 |
45 | private
46 |
47 | def method_missing(method_name, *args)
48 | data_key = data_key_for(method_name)
49 | if data_key
50 | @data[data_key]
51 | else
52 | super
53 | end
54 | end
55 |
56 | def respond_to_missing?(method_name, _include_private = false)
57 | !data_key_for(method_name).nil?
58 | end
59 |
60 | def data_key_for(name)
61 | possible_data_keys_for(name).find { |k| @data.key?(k) }
62 | end
63 |
64 | def possible_data_keys_for(key)
65 | [key.to_s, convert_to_attr_key(key)]
66 | end
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/spec/features/credit_card_payment_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'integration_spec_helper'
3 |
4 | RSpec.describe 'credit card payment', type: :feature, js: true, ngrok: true do
5 | before do
6 | NotifyCallbackTesting = double
7 | allow(NotifyCallbackTesting).to receive(:response) { |response| @notify_callback_response = response }
8 | end
9 | before do
10 | MPGCallbackTesting = double
11 | allow(MPGCallbackTesting).to receive(:response) { |response| @mpg_callback_response = response }
12 | end
13 |
14 | scenario 'User pays with credit card' do
15 | page.driver.browser.js_errors = false
16 |
17 | visit pages_credit_card_payment_button_path
18 | click_button 'Go pay'
19 |
20 | expect(find(:css, '#payer_mail').value).to eq('test@example.com')
21 |
22 | fill_in 'card_1', with: '4000'
23 | fill_in 'card_2', with: '2211'
24 | fill_in 'card_3', with: '1111'
25 | fill_in 'card_4', with: '1111'
26 |
27 | select '01', from: 'expire_m'
28 | select '32', from: 'expire_y'
29 |
30 | fill_in 'cvc', with: '111'
31 |
32 | check 'confirm_order'
33 |
34 | find(:css, '.put_send_btn .btn').click
35 |
36 | wait_for_ajax
37 |
38 | wait_for_content 'DONE', max_wait_time: 10
39 |
40 | expect(@notify_callback_response.status).to eq('SUCCESS')
41 | expect(@notify_callback_response.result.Card6No).to eq('400022')
42 | expect(@notify_callback_response.result.Card4No).to eq('1111')
43 | expect(@notify_callback_response.result.Exp).to eq('3201')
44 |
45 | expect(@mpg_callback_response.status).to eq('SUCCESS')
46 | expect(@mpg_callback_response.result.Card6No).to eq('400022')
47 | expect(@mpg_callback_response.result.Card4No).to eq('1111')
48 | expect(@mpg_callback_response.result.Exp).to eq('3201')
49 |
50 | expect(page).to have_content('Payment has been proceeded.')
51 |
52 | data = JSON.parse(find(:css, '#data').text)
53 |
54 | expect(data['spgateway_response']['status']).to eq('SUCCESS')
55 | expect(data['spgateway_response']['result']['data']['Card6No']).to eq('400022')
56 | expect(data['spgateway_response']['result']['data']['Card4No']).to eq('1111')
57 | expect(data['spgateway_response']['result']['data']['Exp']).to eq('3201')
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/spec/test_app/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | threads threads_count, threads_count
9 |
10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11 | #
12 | port ENV.fetch("PORT") { 3000 }
13 |
14 | # Specifies the `environment` that Puma will run in.
15 | #
16 | environment ENV.fetch("RAILS_ENV") { "development" }
17 |
18 | # Specifies the number of `workers` to boot in clustered mode.
19 | # Workers are forked webserver processes. If using threads and workers together
20 | # the concurrency of the application would be max `threads` * `workers`.
21 | # Workers do not work on JRuby or Windows (both of which do not support
22 | # processes).
23 | #
24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25 |
26 | # Use the `preload_app!` method when specifying a `workers` number.
27 | # This directive tells Puma to first boot the application and load code
28 | # before forking the application. This takes advantage of Copy On Write
29 | # process behavior so workers use less memory. If you use this option
30 | # you need to make sure to reconnect any threads in the `on_worker_boot`
31 | # block.
32 | #
33 | # preload_app!
34 |
35 | # If you are preloading your application and using Active Record, it's
36 | # recommended that you close any connections to the database before workers
37 | # are forked to prevent connection leakage.
38 | #
39 | # before_fork do
40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
41 | # end
42 |
43 | # The code in the `on_worker_boot` will be called if you are using
44 | # clustered mode by specifying a number of `workers`. After each worker
45 | # process is booted, this block will be run. If you are using the `preload_app!`
46 | # option, you will want to use this block to reconnect to any threads
47 | # or connections that may have been created at application boot, as Ruby
48 | # cannot share connections between processes.
49 | #
50 | # on_worker_boot do
51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
52 | # end
53 | #
54 |
55 | # Allow puma to be restarted by `rails restart` command.
56 | plugin :tmp_restart
57 |
--------------------------------------------------------------------------------
/lib/spgateway/mpg_form.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'spgateway/attr_key_helper'
3 |
4 | module Spgateway
5 | class MPGForm
6 | include AttrKeyHelper
7 |
8 | REQUIRED_ATTRS = %w(Version MerchantID MerchantOrderNo ItemDesc Amt TimeStamp RespondType).freeze
9 |
10 | def initialize(attrs)
11 | unless attrs.is_a? Hash
12 | raise ArgumentError, "When initializing #{self.class.name}, you must pass a hash as an argument."
13 | end
14 |
15 | @attrs = {}
16 | missing_attrs = REQUIRED_ATTRS.map(&:clone)
17 |
18 | attrs.each_pair do |k, v|
19 | key = k.to_s
20 | value = v.to_s
21 | missing_attrs.delete(key)
22 | @attrs[key] = value
23 | end
24 |
25 | if @attrs['Version'].nil?
26 | @attrs['Version'] = '1.2'
27 | missing_attrs.delete('Version')
28 | end
29 |
30 | if @attrs['MerchantID'].nil?
31 | @attrs['MerchantID'] = Spgateway.config.merchant_id
32 | missing_attrs.delete('MerchantID')
33 | end
34 |
35 | if @attrs['TimeStamp'].nil?
36 | @attrs['TimeStamp'] = Time.now.to_i
37 | missing_attrs.delete('TimeStamp')
38 | end
39 |
40 | if @attrs['RespondType'].nil?
41 | @attrs['RespondType'] = 'JSON'
42 | missing_attrs.delete('RespondType')
43 | end
44 |
45 | return self if missing_attrs.count.zero?
46 | raise ArgumentError, "The required attrs: #{missing_attrs.map { |s| "'#{s}'" }.join(', ')} #{missing_attrs.count > 1 ? 'are' : 'is'} missing."
47 | end
48 |
49 | def set_attr(name, value)
50 | @attrs[name] = value
51 | end
52 |
53 | def sorted_attrs
54 | @attrs.sort
55 | end
56 |
57 | def to_s
58 | sorted_attrs.map { |k, v| "#{k}=#{v}" }.join('&')
59 | end
60 |
61 | def check_value
62 | data = "Amt=#{@attrs['Amt']}&MerchantID=#{@attrs['MerchantID']}&MerchantOrderNo=#{@attrs['MerchantOrderNo']}&TimeStamp=#{@attrs['TimeStamp']}&Version=#{@attrs['Version']}"
63 | Spgateway::SHA256.hash(data)
64 | end
65 |
66 | alias CheckValue check_value
67 |
68 | private
69 |
70 | def method_missing(method_name, *args)
71 | attr_key = convert_to_attr_key(method_name)
72 |
73 | if @attrs.key?(attr_key)
74 | @attrs[attr_key]
75 | elsif attr_key.end_with?('=') && args[0]
76 | @attrs[attr_key.chomp('=')] = args[0]
77 | else
78 | super
79 | end
80 | end
81 |
82 | def respond_to_missing?(method_name, _include_private = false)
83 | attr_key = convert_to_attr_key(method_name)
84 |
85 | attr_key.end_with?('=') || @attrs.key?(attr_key) || sup
86 | end
87 | end
88 | end
89 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # This file is copied to spec/ when you run 'rails generate rspec:install'
3 | require 'spec_helper'
4 | ENV['RAILS_ENV'] ||= 'test'
5 | # require File.expand_path('../../config/environment', __FILE__)
6 | # Prevent database truncation if the environment is production
7 | abort('The Rails environment is running in production mode!') if Rails.env.production?
8 | require 'rspec/rails'
9 | # Add additional requires below this line. Rails is not loaded until this point!
10 |
11 | # Requires supporting ruby files with custom matchers and macros, etc, in
12 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
13 | # run as spec files by default. This means that files in spec/support that end
14 | # in _spec.rb will both be required and run as specs, causing the specs to be
15 | # run twice. It is recommended that you do not name files matching this glob to
16 | # end with _spec.rb. You can configure this pattern with the --pattern
17 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
18 | #
19 | # The following line is provided for convenience purposes. It has the downside
20 | # of increasing the boot-up time by auto-requiring all files in the support
21 | # directory. Alternatively, in the individual `*_spec.rb` files, manually
22 | # require only the support files necessary.
23 | #
24 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
25 |
26 | # Checks for pending migration and applies them before tests are run.
27 | # If you are not using ActiveRecord, you can remove this line.
28 | # ActiveRecord::Migration.maintain_test_schema!
29 |
30 | RSpec.configure do |config|
31 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
32 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
33 |
34 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
35 | # examples within a transaction, remove the following line or assign false
36 | # instead of true.
37 | config.use_transactional_fixtures = true
38 |
39 | # RSpec Rails can automatically mix in different behaviours to your tests
40 | # based on their file location, for example enabling you to call `get` and
41 | # `post` in specs under `spec/controllers`.
42 | #
43 | # You can disable this behaviour by removing the line below, and instead
44 | # explicitly tag your specs with their type, e.g.:
45 | #
46 | # RSpec.describe UsersController, :type => :controller do
47 | # # ...
48 | # end
49 | #
50 | # The different available types are documented in the features, such as in
51 | # https://relishapp.com/rspec/rspec-rails/docs
52 | config.infer_spec_type_from_file_location!
53 |
54 | # Filter lines from Rails gems in backtraces.
55 | config.filter_rails_from_backtrace!
56 | # arbitrary gems may also be filtered via:
57 | # config.filter_gems_from_backtrace("gem name")
58 | end
59 |
--------------------------------------------------------------------------------
/spec/test_app/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`.
18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or
19 | # `config/secrets.yml.key`.
20 | config.read_encrypted_secrets = true
21 |
22 | # Disable serving static files from the `/public` folder by default since
23 | # Apache or NGINX already handles this.
24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
25 |
26 | # Compress CSS.
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
33 |
34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
35 | # config.action_controller.asset_host = 'http://assets.example.com'
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
40 |
41 | # Mount Action Cable outside main process or domain
42 | # config.action_cable.mount_path = nil
43 | # config.action_cable.url = 'wss://example.com/cable'
44 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
45 |
46 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
47 | # config.force_ssl = true
48 |
49 | # Use the lowest log level to ensure availability of diagnostic information
50 | # when problems arise.
51 | config.log_level = :debug
52 |
53 | # Prepend all log lines with the following tags.
54 | config.log_tags = [ :request_id ]
55 |
56 | # Use a different cache store in production.
57 | # config.cache_store = :mem_cache_store
58 |
59 | # Use a real queuing backend for Active Job (and separate queues per environment)
60 | # config.active_job.queue_adapter = :resque
61 | # config.active_job.queue_name_prefix = "test_app_#{Rails.env}"
62 |
63 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
64 | # the I18n.default_locale when a translation cannot be found).
65 | config.i18n.fallbacks = true
66 |
67 | # Send deprecation notices to registered listeners.
68 | config.active_support.deprecation = :notify
69 |
70 | # Use default logging formatter so that PID and timestamp are not suppressed.
71 | config.log_formatter = ::Logger::Formatter.new
72 |
73 | # Use a different logger for distributed setups.
74 | # require 'syslog/logger'
75 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
76 |
77 | if ENV["RAILS_LOG_TO_STDOUT"].present?
78 | logger = ActiveSupport::Logger.new(STDOUT)
79 | logger.formatter = config.log_formatter
80 | config.logger = ActiveSupport::TaggedLogging.new(logger)
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/app/helpers/spgateway/application_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | module Spgateway
3 | module ApplicationHelper
4 | def spgateway_mpg_form_for(
5 | mpg_form_object,
6 | submit: 'Go',
7 | submit_class: '',
8 | mpg_gateway_url: Spgateway.config.mpg_gateway_url
9 | )
10 | unless mpg_form_object.is_a? Spgateway::MPGForm
11 | raise ArgumentError, "The first argument for 'pay2goo_form_for' must be a Spgateway::MPGForm."
12 | end
13 |
14 | form_tag(mpg_gateway_url, method: :post) do
15 | mpg_form_object.sorted_attrs.each do |param_pair|
16 | name, value = param_pair
17 | concat hidden_field_tag name, value
18 | end
19 |
20 | concat hidden_field_tag :CheckValue, mpg_form_object.check_value
21 |
22 | concat submit_tag submit, class: submit_class
23 | end
24 | end
25 |
26 | def spgateway_pay_button(title, options)
27 | raise ArgumentError, 'Missing required argument: order_number.' unless options[:order_number]
28 | raise ArgumentError, 'Missing required argument: item_description.' unless options[:item_description]
29 | raise ArgumentError, 'Missing required argument: amount.' unless options[:amount]
30 |
31 | form_attributes = options.except(
32 | :order_number,
33 | :item_description,
34 | :amount,
35 | :payer_email,
36 | :payment_methods,
37 | :class,
38 | :mpg_gateway_url
39 | )
40 |
41 | form_attributes[:MerchantOrderNo] = options[:order_number]
42 | form_attributes[:ItemDesc] = options[:item_description]
43 | form_attributes[:Amt] = options[:amount]
44 | form_attributes[:Email] = options[:payer_email]
45 |
46 | form = Spgateway::MPGForm.new(form_attributes)
47 |
48 | form.return_url =
49 | Spgateway::Engine.routes.url_helpers.mpg_callbacks_url(host: request.host, port: request.port)
50 |
51 | if [80, 443].include?(request.port) && Spgateway.config.payment_code_callback
52 | form.customer_url =
53 | Spgateway::Engine.routes.url_helpers.payment_code_callbacks_url(host: request.host, port: request.port)
54 | end
55 |
56 | if [80, 443].include?(request.port) && Spgateway.config.notify_callback
57 | form.notify_url =
58 | Spgateway::Engine.routes.url_helpers.notify_callbacks_url(host: request.host, port: request.port)
59 | end
60 |
61 | if options[:payment_methods].is_a? Array
62 | options[:payment_methods].each do |payment_method|
63 | case payment_method.to_sym
64 | when :credit
65 | form.set_attr 'CREDIT', '1'
66 | when :credit_card
67 | form.set_attr 'CREDIT', '1'
68 | when :inst_flag
69 | form.set_attr 'InstFlag', '1'
70 | when :installment
71 | form.set_attr 'InstFlag', '1'
72 | when :credit_red
73 | form.set_attr 'CreditRed', '1'
74 | when :unionpay
75 | form.set_attr 'UNIONPAY', '1'
76 | when :webatm
77 | form.set_attr 'WEBATM', '1'
78 | when :vacc
79 | form.set_attr 'VACC', '1'
80 | when :cvs
81 | form.set_attr 'CVS', '1'
82 | when :barcode
83 | form.set_attr 'BARCODE', '1'
84 | else
85 | form.set_attr payment_method, '1'
86 | end
87 | end
88 | end
89 |
90 | spgateway_mpg_form_for(
91 | form,
92 | submit: title,
93 | submit_class: options[:class],
94 | mpg_gateway_url: options[:mpg_gateway_url] || Spgateway.config.mpg_gateway_url
95 | )
96 | end
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/lib/generators/spgateway/templates/spgateway_initializer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | Spgateway.configure do |config|
3 | # Spgateway merchant ID.
4 | # You can get your ID on https://www.spgateway.com/shop/member_shop/shop_list
5 | # (or https://cwww.spgateway.com/shop/member_shop/shop_list for development).
6 | config.merchant_id = 'place_your_merchant_id_here'
7 |
8 | # The HashKey and HashIV of the merchant on Spgateway. This can be found in
9 | # the「銷售中心 › 商店管理 › 商店資料設定,詳細資料 › API 串接金鑰」section of the
10 | # Spgateway member centre.
11 | config.hash_key = 'place_your_hash_key_here'
12 | config.hash_iv = 'place_your_hash_iv_here'
13 |
14 | # Spgateway API URL.
15 | # Normally will be 'https://ccore.spgateway.com/API' for development and
16 | # 'https://core.spgateway.com/API' for production.
17 | config.api_url = 'https://ccore.spgateway.com/API'
18 |
19 | # MPG gateway URL (i.e. the "action" attribute in user's purchasing form).
20 | # Normally this will be 'https://ccore.spgateway.com/MPG/mpg_gateway' for
21 | # development and 'https://core.spgateway.com/MPG/mpg_gateway' for
22 | # production.
23 | config.mpg_gateway_url = 'https://ccore.spgateway.com/MPG/mpg_gateway'
24 |
25 | # Callback after the user has been redirect back from Spgateway MPG gateway.
26 | config.mpg_callback do |spgateway_response|
27 | raise "Please configure mpg_callback in #{__FILE__}"
28 | # Put the trade result user facing logic here.
29 | #
30 | # Be careful not to repeat the order data updating work if you've configure
31 | # notify_callback to do it - because both notify_callback and mpg_callback
32 | # will be called when the user has done with the payment (notify_callback)
33 | # and then be redirected back to the website immediately (mpg_callback),
34 | # unexpected results might happen if you do the same thing twice at the
35 | # same time.
36 | #
37 | # Implementation example:
38 | # (this only shows the results to the user while we assume that you want
39 | # notify_callback - placed at the next section of this file - to do the
40 | # real business logic)
41 | #
42 | # if spgateway_response.status == 'SUCCESS'
43 | # flash[:success] = spgateway_response.message
44 | # else
45 | # flash[:error] = spgateway_response.message
46 | # end
47 | #
48 | # redirect_to orders_path
49 | end
50 |
51 | # Callback triggered by Spgateway after an order has been paid.
52 | # You can ignore this config if you're not using offline payment methods and
53 | # had done everything in mpg_callback.
54 | # config.notify_callback do |spgateway_response|
55 | # # Put the trade result proceeding logic here.
56 | # #
57 | # # Implementation example:
58 | #
59 | # if spgateway_response.status == 'SUCCESS'
60 | # Order.find_by(serial: spgateway_response.result.merchant_order_no)
61 | # .update_attributes!(paid: true)
62 | # else
63 | # Rails.logger.info "Spgateway Payment Not Succeed: #{spgateway_response.status}: #{spgateway_response.message} (#{spgateway_response.result.to_json})"
64 | # end
65 | # end
66 |
67 | # Callback when received the ATM transfer account, convenience store payment
68 | # code or barcode.
69 | # If you want Spgateway to display these payment instructions directly to
70 | # your customers directly, simply ignore this config.
71 | # config.payment_code_callback do |spgateway_response|
72 | # # Put the trade result proceeding logic here.
73 | # #
74 | # # Implementation example:
75 | # #
76 | # if spgateway_response.status == 'SUCCESS' &&
77 | # spgateway_response.result.payment_type == 'VACC'
78 |
79 | # bank_code = spgateway_response.result.bank_code
80 | # account_number = spgateway_response.result.code_no
81 | # expired_at =
82 | # DateTime.parse("#{spgateway_response.result.expire_date} #{spgateway_response.result.expire_time} UTC+8")
83 | # Order.find_by(serial: spgateway_response.result.merchant_order_no)
84 | # .update_attributes!(bank_code: bank_code, account_number: account_number, expired_at: expired_at)
85 | # flash[:info] =
86 | # "Please transfer the money to bank code #{bank_code}, account number #{account_number} before #{I18n.l(expired_at)}"
87 | # else
88 | # Rails.logger.error "Spgateway Payment Code Receive Not Succeed: #{spgateway_response.status}: #{spgateway_response.message} (#{spgateway_response.result.to_json})"
89 | # flash[:error] = "Our apologies, but an unexpected error occured, please try again"
90 | # end
91 |
92 | # redirect_to orders_path
93 | # end
94 | end
95 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | spgateway-rails (0.1.0)
5 | rails (>= 4.0, < 5.2)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (5.1.0)
11 | actionpack (= 5.1.0)
12 | nio4r (~> 2.0)
13 | websocket-driver (~> 0.6.1)
14 | actionmailer (5.1.0)
15 | actionpack (= 5.1.0)
16 | actionview (= 5.1.0)
17 | activejob (= 5.1.0)
18 | mail (~> 2.5, >= 2.5.4)
19 | rails-dom-testing (~> 2.0)
20 | actionpack (5.1.0)
21 | actionview (= 5.1.0)
22 | activesupport (= 5.1.0)
23 | rack (~> 2.0)
24 | rack-test (~> 0.6.3)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
27 | actionview (5.1.0)
28 | activesupport (= 5.1.0)
29 | builder (~> 3.1)
30 | erubi (~> 1.4)
31 | rails-dom-testing (~> 2.0)
32 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
33 | activejob (5.1.0)
34 | activesupport (= 5.1.0)
35 | globalid (>= 0.3.6)
36 | activemodel (5.1.0)
37 | activesupport (= 5.1.0)
38 | activerecord (5.1.0)
39 | activemodel (= 5.1.0)
40 | activesupport (= 5.1.0)
41 | arel (~> 8.0)
42 | activesupport (5.1.0)
43 | concurrent-ruby (~> 1.0, >= 1.0.2)
44 | i18n (~> 0.7)
45 | minitest (~> 5.1)
46 | tzinfo (~> 1.1)
47 | addressable (2.5.1)
48 | public_suffix (~> 2.0, >= 2.0.2)
49 | appraisal (2.2.0)
50 | bundler
51 | rake
52 | thor (>= 0.14.0)
53 | arel (8.0.0)
54 | builder (3.2.3)
55 | capybara (2.14.0)
56 | addressable
57 | mime-types (>= 1.16)
58 | nokogiri (>= 1.3.3)
59 | rack (>= 1.0.0)
60 | rack-test (>= 0.5.4)
61 | xpath (~> 2.0)
62 | cliver (0.3.2)
63 | concurrent-ruby (1.0.5)
64 | coveralls (0.7.1)
65 | multi_json (~> 1.3)
66 | rest-client
67 | simplecov (>= 0.7)
68 | term-ansicolor
69 | thor
70 | diff-lcs (1.3)
71 | docile (1.1.5)
72 | domain_name (0.5.20161129)
73 | unf (>= 0.0.5, < 1.0.0)
74 | erubi (1.6.0)
75 | globalid (0.4.0)
76 | activesupport (>= 4.2.0)
77 | http-cookie (1.0.3)
78 | domain_name (~> 0.5)
79 | i18n (0.8.1)
80 | json (2.0.4)
81 | loofah (2.0.3)
82 | nokogiri (>= 1.5.9)
83 | mail (2.6.5)
84 | mime-types (>= 1.16, < 4)
85 | method_source (0.8.2)
86 | mime-types (3.1)
87 | mime-types-data (~> 3.2015)
88 | mime-types-data (3.2016.0521)
89 | mini_portile2 (2.1.0)
90 | minitest (5.10.2)
91 | multi_json (1.12.1)
92 | netrc (0.11.0)
93 | ngrok-rspec (1.1.18)
94 | capybara (>= 2.4)
95 | ngrok-tunnel (~> 2.0)
96 | rspec (>= 2.99)
97 | ngrok-tunnel (2.1.0)
98 | nio4r (2.0.0)
99 | nokogiri (1.7.2)
100 | mini_portile2 (~> 2.1.0)
101 | poltergeist (1.15.0)
102 | capybara (~> 2.1)
103 | cliver (~> 0.3.1)
104 | websocket-driver (>= 0.2.0)
105 | public_suffix (2.0.5)
106 | rack (2.0.2)
107 | rack-test (0.6.3)
108 | rack (>= 1.0)
109 | rails (5.1.0)
110 | actioncable (= 5.1.0)
111 | actionmailer (= 5.1.0)
112 | actionpack (= 5.1.0)
113 | actionview (= 5.1.0)
114 | activejob (= 5.1.0)
115 | activemodel (= 5.1.0)
116 | activerecord (= 5.1.0)
117 | activesupport (= 5.1.0)
118 | bundler (>= 1.3.0, < 2.0)
119 | railties (= 5.1.0)
120 | sprockets-rails (>= 2.0.0)
121 | rails-dom-testing (2.0.2)
122 | activesupport (>= 4.2.0, < 6.0)
123 | nokogiri (~> 1.6)
124 | rails-html-sanitizer (1.0.3)
125 | loofah (~> 2.0)
126 | railties (5.1.0)
127 | actionpack (= 5.1.0)
128 | activesupport (= 5.1.0)
129 | method_source
130 | rake (>= 0.8.7)
131 | thor (>= 0.18.1, < 2.0)
132 | rake (12.0.0)
133 | rest-client (2.0.0)
134 | http-cookie (>= 1.0.2, < 2.0)
135 | mime-types (>= 1.16, < 4.0)
136 | netrc (~> 0.8)
137 | rspec (3.6.0)
138 | rspec-core (~> 3.6.0)
139 | rspec-expectations (~> 3.6.0)
140 | rspec-mocks (~> 3.6.0)
141 | rspec-core (3.6.0)
142 | rspec-support (~> 3.6.0)
143 | rspec-expectations (3.6.0)
144 | diff-lcs (>= 1.2.0, < 2.0)
145 | rspec-support (~> 3.6.0)
146 | rspec-mocks (3.6.0)
147 | diff-lcs (>= 1.2.0, < 2.0)
148 | rspec-support (~> 3.6.0)
149 | rspec-rails (3.6.0)
150 | actionpack (>= 3.0)
151 | activesupport (>= 3.0)
152 | railties (>= 3.0)
153 | rspec-core (~> 3.6.0)
154 | rspec-expectations (~> 3.6.0)
155 | rspec-mocks (~> 3.6.0)
156 | rspec-support (~> 3.6.0)
157 | rspec-support (3.6.0)
158 | simplecov (0.13.0)
159 | docile (~> 1.1.0)
160 | json (>= 1.8, < 3)
161 | simplecov-html (~> 0.10.0)
162 | simplecov-html (0.10.0)
163 | sprockets (3.7.1)
164 | concurrent-ruby (~> 1.0)
165 | rack (> 1, < 3)
166 | sprockets-rails (3.2.0)
167 | actionpack (>= 4.0)
168 | activesupport (>= 4.0)
169 | sprockets (>= 3.0.0)
170 | term-ansicolor (1.4.0)
171 | tins (~> 1.0)
172 | thor (0.19.4)
173 | thread_safe (0.3.6)
174 | tins (1.13.2)
175 | tzinfo (1.2.3)
176 | thread_safe (~> 0.1)
177 | unf (0.1.4)
178 | unf_ext
179 | unf_ext (0.0.7.2)
180 | websocket-driver (0.6.5)
181 | websocket-extensions (>= 0.1.0)
182 | websocket-extensions (0.1.2)
183 | xpath (2.0.0)
184 | nokogiri (~> 1.3)
185 |
186 | PLATFORMS
187 | ruby
188 |
189 | DEPENDENCIES
190 | appraisal
191 | capybara
192 | coveralls
193 | ngrok-rspec
194 | poltergeist
195 | rspec-rails
196 | simplecov
197 | spgateway-rails!
198 |
199 | BUNDLED WITH
200 | 1.13.7
201 |
--------------------------------------------------------------------------------
/gemfiles/rails_4.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ../
3 | specs:
4 | spgateway-rails (0.1.0)
5 | rails (>= 4.0, < 5.2)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actionmailer (4.2.0)
11 | actionpack (= 4.2.0)
12 | actionview (= 4.2.0)
13 | activejob (= 4.2.0)
14 | mail (~> 2.5, >= 2.5.4)
15 | rails-dom-testing (~> 1.0, >= 1.0.5)
16 | actionpack (4.2.0)
17 | actionview (= 4.2.0)
18 | activesupport (= 4.2.0)
19 | rack (~> 1.6.0)
20 | rack-test (~> 0.6.2)
21 | rails-dom-testing (~> 1.0, >= 1.0.5)
22 | rails-html-sanitizer (~> 1.0, >= 1.0.1)
23 | actionview (4.2.0)
24 | activesupport (= 4.2.0)
25 | builder (~> 3.1)
26 | erubis (~> 2.7.0)
27 | rails-dom-testing (~> 1.0, >= 1.0.5)
28 | rails-html-sanitizer (~> 1.0, >= 1.0.1)
29 | activejob (4.2.0)
30 | activesupport (= 4.2.0)
31 | globalid (>= 0.3.0)
32 | activemodel (4.2.0)
33 | activesupport (= 4.2.0)
34 | builder (~> 3.1)
35 | activerecord (4.2.0)
36 | activemodel (= 4.2.0)
37 | activesupport (= 4.2.0)
38 | arel (~> 6.0)
39 | activesupport (4.2.0)
40 | i18n (~> 0.7)
41 | json (~> 1.7, >= 1.7.7)
42 | minitest (~> 5.1)
43 | thread_safe (~> 0.3, >= 0.3.4)
44 | tzinfo (~> 1.1)
45 | addressable (2.5.1)
46 | public_suffix (~> 2.0, >= 2.0.2)
47 | appraisal (2.2.0)
48 | bundler
49 | rake
50 | thor (>= 0.14.0)
51 | arel (6.0.4)
52 | builder (3.2.3)
53 | capybara (2.14.0)
54 | addressable
55 | mime-types (>= 1.16)
56 | nokogiri (>= 1.3.3)
57 | rack (>= 1.0.0)
58 | rack-test (>= 0.5.4)
59 | xpath (~> 2.0)
60 | cliver (0.3.2)
61 | concurrent-ruby (1.0.5)
62 | coveralls (0.7.1)
63 | multi_json (~> 1.3)
64 | rest-client
65 | simplecov (>= 0.7)
66 | term-ansicolor
67 | thor
68 | diff-lcs (1.3)
69 | docile (1.1.5)
70 | domain_name (0.5.20161129)
71 | unf (>= 0.0.5, < 1.0.0)
72 | erubis (2.7.0)
73 | globalid (0.4.0)
74 | activesupport (>= 4.2.0)
75 | http-cookie (1.0.3)
76 | domain_name (~> 0.5)
77 | i18n (0.8.1)
78 | json (1.8.6)
79 | loofah (2.0.3)
80 | nokogiri (>= 1.5.9)
81 | mail (2.6.5)
82 | mime-types (>= 1.16, < 4)
83 | mime-types (3.1)
84 | mime-types-data (~> 3.2015)
85 | mime-types-data (3.2016.0521)
86 | mini_portile2 (2.1.0)
87 | minitest (5.10.2)
88 | multi_json (1.12.1)
89 | netrc (0.11.0)
90 | ngrok-rspec (1.1.18)
91 | capybara (>= 2.4)
92 | ngrok-tunnel (~> 2.0)
93 | rspec (>= 2.99)
94 | ngrok-tunnel (2.1.0)
95 | nokogiri (1.7.2)
96 | mini_portile2 (~> 2.1.0)
97 | poltergeist (1.15.0)
98 | capybara (~> 2.1)
99 | cliver (~> 0.3.1)
100 | websocket-driver (>= 0.2.0)
101 | public_suffix (2.0.5)
102 | rack (1.6.6)
103 | rack-test (0.6.3)
104 | rack (>= 1.0)
105 | rails (4.2.0)
106 | actionmailer (= 4.2.0)
107 | actionpack (= 4.2.0)
108 | actionview (= 4.2.0)
109 | activejob (= 4.2.0)
110 | activemodel (= 4.2.0)
111 | activerecord (= 4.2.0)
112 | activesupport (= 4.2.0)
113 | bundler (>= 1.3.0, < 2.0)
114 | railties (= 4.2.0)
115 | sprockets-rails
116 | rails-deprecated_sanitizer (1.0.3)
117 | activesupport (>= 4.2.0.alpha)
118 | rails-dom-testing (1.0.8)
119 | activesupport (>= 4.2.0.beta, < 5.0)
120 | nokogiri (~> 1.6)
121 | rails-deprecated_sanitizer (>= 1.0.1)
122 | rails-html-sanitizer (1.0.3)
123 | loofah (~> 2.0)
124 | railties (4.2.0)
125 | actionpack (= 4.2.0)
126 | activesupport (= 4.2.0)
127 | rake (>= 0.8.7)
128 | thor (>= 0.18.1, < 2.0)
129 | rake (12.0.0)
130 | rest-client (2.0.0)
131 | http-cookie (>= 1.0.2, < 2.0)
132 | mime-types (>= 1.16, < 4.0)
133 | netrc (~> 0.8)
134 | rspec (3.6.0)
135 | rspec-core (~> 3.6.0)
136 | rspec-expectations (~> 3.6.0)
137 | rspec-mocks (~> 3.6.0)
138 | rspec-core (3.6.0)
139 | rspec-support (~> 3.6.0)
140 | rspec-expectations (3.6.0)
141 | diff-lcs (>= 1.2.0, < 2.0)
142 | rspec-support (~> 3.6.0)
143 | rspec-mocks (3.6.0)
144 | diff-lcs (>= 1.2.0, < 2.0)
145 | rspec-support (~> 3.6.0)
146 | rspec-rails (3.6.0)
147 | actionpack (>= 3.0)
148 | activesupport (>= 3.0)
149 | railties (>= 3.0)
150 | rspec-core (~> 3.6.0)
151 | rspec-expectations (~> 3.6.0)
152 | rspec-mocks (~> 3.6.0)
153 | rspec-support (~> 3.6.0)
154 | rspec-support (3.6.0)
155 | simplecov (0.13.0)
156 | docile (~> 1.1.0)
157 | json (>= 1.8, < 3)
158 | simplecov-html (~> 0.10.0)
159 | simplecov-html (0.10.0)
160 | sprockets (3.7.1)
161 | concurrent-ruby (~> 1.0)
162 | rack (> 1, < 3)
163 | sprockets-rails (3.2.0)
164 | actionpack (>= 4.0)
165 | activesupport (>= 4.0)
166 | sprockets (>= 3.0.0)
167 | term-ansicolor (1.4.0)
168 | tins (~> 1.0)
169 | thor (0.19.4)
170 | thread_safe (0.3.6)
171 | tins (1.13.2)
172 | tzinfo (1.2.3)
173 | thread_safe (~> 0.1)
174 | unf (0.1.4)
175 | unf_ext
176 | unf_ext (0.0.7.2)
177 | websocket-driver (0.6.5)
178 | websocket-extensions (>= 0.1.0)
179 | websocket-extensions (0.1.2)
180 | xpath (2.0.0)
181 | nokogiri (~> 1.3)
182 |
183 | PLATFORMS
184 | ruby
185 |
186 | DEPENDENCIES
187 | appraisal
188 | capybara
189 | coveralls
190 | ngrok-rspec
191 | poltergeist
192 | rails (= 4.2)
193 | rspec-rails
194 | simplecov
195 | spgateway-rails!
196 |
197 | BUNDLED WITH
198 | 1.13.7
199 |
--------------------------------------------------------------------------------
/gemfiles/rails_5.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ../
3 | specs:
4 | spgateway-rails (0.1.0)
5 | rails (>= 4.0, < 5.2)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (5.1.0)
11 | actionpack (= 5.1.0)
12 | nio4r (~> 2.0)
13 | websocket-driver (~> 0.6.1)
14 | actionmailer (5.1.0)
15 | actionpack (= 5.1.0)
16 | actionview (= 5.1.0)
17 | activejob (= 5.1.0)
18 | mail (~> 2.5, >= 2.5.4)
19 | rails-dom-testing (~> 2.0)
20 | actionpack (5.1.0)
21 | actionview (= 5.1.0)
22 | activesupport (= 5.1.0)
23 | rack (~> 2.0)
24 | rack-test (~> 0.6.3)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
27 | actionview (5.1.0)
28 | activesupport (= 5.1.0)
29 | builder (~> 3.1)
30 | erubi (~> 1.4)
31 | rails-dom-testing (~> 2.0)
32 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
33 | activejob (5.1.0)
34 | activesupport (= 5.1.0)
35 | globalid (>= 0.3.6)
36 | activemodel (5.1.0)
37 | activesupport (= 5.1.0)
38 | activerecord (5.1.0)
39 | activemodel (= 5.1.0)
40 | activesupport (= 5.1.0)
41 | arel (~> 8.0)
42 | activesupport (5.1.0)
43 | concurrent-ruby (~> 1.0, >= 1.0.2)
44 | i18n (~> 0.7)
45 | minitest (~> 5.1)
46 | tzinfo (~> 1.1)
47 | addressable (2.5.1)
48 | public_suffix (~> 2.0, >= 2.0.2)
49 | appraisal (2.2.0)
50 | bundler
51 | rake
52 | thor (>= 0.14.0)
53 | arel (8.0.0)
54 | builder (3.2.3)
55 | capybara (2.14.0)
56 | addressable
57 | mime-types (>= 1.16)
58 | nokogiri (>= 1.3.3)
59 | rack (>= 1.0.0)
60 | rack-test (>= 0.5.4)
61 | xpath (~> 2.0)
62 | cliver (0.3.2)
63 | concurrent-ruby (1.0.5)
64 | coveralls (0.7.1)
65 | multi_json (~> 1.3)
66 | rest-client
67 | simplecov (>= 0.7)
68 | term-ansicolor
69 | thor
70 | diff-lcs (1.3)
71 | docile (1.1.5)
72 | domain_name (0.5.20161129)
73 | unf (>= 0.0.5, < 1.0.0)
74 | erubi (1.6.0)
75 | globalid (0.4.0)
76 | activesupport (>= 4.2.0)
77 | http-cookie (1.0.3)
78 | domain_name (~> 0.5)
79 | i18n (0.8.1)
80 | json (2.0.4)
81 | loofah (2.0.3)
82 | nokogiri (>= 1.5.9)
83 | mail (2.6.5)
84 | mime-types (>= 1.16, < 4)
85 | method_source (0.8.2)
86 | mime-types (3.1)
87 | mime-types-data (~> 3.2015)
88 | mime-types-data (3.2016.0521)
89 | mini_portile2 (2.1.0)
90 | minitest (5.10.2)
91 | multi_json (1.12.1)
92 | netrc (0.11.0)
93 | ngrok-rspec (1.1.18)
94 | capybara (>= 2.4)
95 | ngrok-tunnel (~> 2.0)
96 | rspec (>= 2.99)
97 | ngrok-tunnel (2.1.0)
98 | nio4r (2.0.0)
99 | nokogiri (1.7.2)
100 | mini_portile2 (~> 2.1.0)
101 | poltergeist (1.15.0)
102 | capybara (~> 2.1)
103 | cliver (~> 0.3.1)
104 | websocket-driver (>= 0.2.0)
105 | public_suffix (2.0.5)
106 | rack (2.0.2)
107 | rack-test (0.6.3)
108 | rack (>= 1.0)
109 | rails (5.1.0)
110 | actioncable (= 5.1.0)
111 | actionmailer (= 5.1.0)
112 | actionpack (= 5.1.0)
113 | actionview (= 5.1.0)
114 | activejob (= 5.1.0)
115 | activemodel (= 5.1.0)
116 | activerecord (= 5.1.0)
117 | activesupport (= 5.1.0)
118 | bundler (>= 1.3.0, < 2.0)
119 | railties (= 5.1.0)
120 | sprockets-rails (>= 2.0.0)
121 | rails-dom-testing (2.0.2)
122 | activesupport (>= 4.2.0, < 6.0)
123 | nokogiri (~> 1.6)
124 | rails-html-sanitizer (1.0.3)
125 | loofah (~> 2.0)
126 | railties (5.1.0)
127 | actionpack (= 5.1.0)
128 | activesupport (= 5.1.0)
129 | method_source
130 | rake (>= 0.8.7)
131 | thor (>= 0.18.1, < 2.0)
132 | rake (12.0.0)
133 | rest-client (2.0.0)
134 | http-cookie (>= 1.0.2, < 2.0)
135 | mime-types (>= 1.16, < 4.0)
136 | netrc (~> 0.8)
137 | rspec (3.6.0)
138 | rspec-core (~> 3.6.0)
139 | rspec-expectations (~> 3.6.0)
140 | rspec-mocks (~> 3.6.0)
141 | rspec-core (3.6.0)
142 | rspec-support (~> 3.6.0)
143 | rspec-expectations (3.6.0)
144 | diff-lcs (>= 1.2.0, < 2.0)
145 | rspec-support (~> 3.6.0)
146 | rspec-mocks (3.6.0)
147 | diff-lcs (>= 1.2.0, < 2.0)
148 | rspec-support (~> 3.6.0)
149 | rspec-rails (3.6.0)
150 | actionpack (>= 3.0)
151 | activesupport (>= 3.0)
152 | railties (>= 3.0)
153 | rspec-core (~> 3.6.0)
154 | rspec-expectations (~> 3.6.0)
155 | rspec-mocks (~> 3.6.0)
156 | rspec-support (~> 3.6.0)
157 | rspec-support (3.6.0)
158 | simplecov (0.13.0)
159 | docile (~> 1.1.0)
160 | json (>= 1.8, < 3)
161 | simplecov-html (~> 0.10.0)
162 | simplecov-html (0.10.0)
163 | sprockets (3.7.1)
164 | concurrent-ruby (~> 1.0)
165 | rack (> 1, < 3)
166 | sprockets-rails (3.2.0)
167 | actionpack (>= 4.0)
168 | activesupport (>= 4.0)
169 | sprockets (>= 3.0.0)
170 | term-ansicolor (1.4.0)
171 | tins (~> 1.0)
172 | thor (0.19.4)
173 | thread_safe (0.3.6)
174 | tins (1.13.2)
175 | tzinfo (1.2.3)
176 | thread_safe (~> 0.1)
177 | unf (0.1.4)
178 | unf_ext
179 | unf_ext (0.0.7.2)
180 | websocket-driver (0.6.5)
181 | websocket-extensions (>= 0.1.0)
182 | websocket-extensions (0.1.2)
183 | xpath (2.0.0)
184 | nokogiri (~> 1.3)
185 |
186 | PLATFORMS
187 | ruby
188 |
189 | DEPENDENCIES
190 | appraisal
191 | capybara
192 | coveralls
193 | ngrok-rspec
194 | poltergeist
195 | rails (= 5.1)
196 | rspec-rails
197 | simplecov
198 | spgateway-rails!
199 |
200 | BUNDLED WITH
201 | 1.13.7
202 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all
3 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
4 | # The generated `.rspec` file contains `--require spec_helper` which will cause
5 | # this file to always be loaded, without a need to explicitly require it in any
6 | # files.
7 | #
8 | # Given that it is always loaded, you are encouraged to keep this file as
9 | # light-weight as possible. Requiring heavyweight dependencies from this file
10 | # will add to the boot time of your test suite on EVERY test run, even for an
11 | # individual file that may not need all of that loaded. Instead, consider making
12 | # a separate helper file that requires the additional dependencies and performs
13 | # the additional setup, and require it from the spec files that actually need
14 | # it.
15 | #
16 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17 |
18 | require 'simplecov'
19 | require 'coveralls'
20 |
21 | SimpleCov.start do
22 | load_profile 'test_frameworks'
23 |
24 | add_group 'Spgateway Lib', 'lib/spgateway'
25 | add_group 'Controllers', 'app/controllers'
26 | add_group 'Helpers', 'app/helper'
27 | add_group 'Jobs', 'app/jobs'
28 |
29 | add_filter 'lib/spgateway/rails/engine.rb'
30 | add_filter 'lib/spgateway/version.rb'
31 | add_filter 'lib/generators/spgateway'
32 | add_filter 'app/jobs'
33 |
34 | track_files '{app,lib}/**/*.rb'
35 | end
36 |
37 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
38 | [
39 | SimpleCov::Formatter::HTMLFormatter,
40 | Coveralls::SimpleCov::Formatter
41 | ]
42 | )
43 |
44 | require 'support/wait_for'
45 |
46 | RSpec.configure do |config|
47 | # rspec-expectations config goes here. You can use an alternate
48 | # assertion/expectation library such as wrong or the stdlib/minitest
49 | # assertions if you prefer.
50 | config.expect_with :rspec do |expectations|
51 | # This option will default to `true` in RSpec 4. It makes the `description`
52 | # and `failure_message` of custom matchers include text for helper methods
53 | # defined using `chain`, e.g.:
54 | # be_bigger_than(2).and_smaller_than(4).description
55 | # # => "be bigger than 2 and smaller than 4"
56 | # ...rather than:
57 | # # => "be bigger than 2"
58 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
59 | end
60 |
61 | # rspec-mocks config goes here. You can use an alternate test double
62 | # library (such as bogus or mocha) by changing the `mock_with` option here.
63 | config.mock_with :rspec do |mocks|
64 | # Prevents you from mocking or stubbing a method that does not exist on
65 | # a real object. This is generally recommended, and will default to
66 | # `true` in RSpec 4.
67 | mocks.verify_partial_doubles = true
68 | end
69 |
70 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
71 | # have no way to turn it off -- the option exists only for backwards
72 | # compatibility in RSpec 3). It causes shared context metadata to be
73 | # inherited by the metadata hash of host groups and examples, rather than
74 | # triggering implicit auto-inclusion in groups with matching metadata.
75 | config.shared_context_metadata_behavior = :apply_to_host_groups
76 |
77 | # The settings below are suggested to provide a good initial experience
78 | # with RSpec, but feel free to customize to your heart's content.
79 | =begin
80 | # This allows you to limit a spec run to individual examples or groups
81 | # you care about by tagging them with `:focus` metadata. When nothing
82 | # is tagged with `:focus`, all examples get run. RSpec also provides
83 | # aliases for `it`, `describe`, and `context` that include `:focus`
84 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
85 | config.filter_run_when_matching :focus
86 |
87 | # Allows RSpec to persist some state between runs in order to support
88 | # the `--only-failures` and `--next-failure` CLI options. We recommend
89 | # you configure your source control system to ignore this file.
90 | config.example_status_persistence_file_path = "spec/examples.txt"
91 |
92 | # Limits the available syntax to the non-monkey patched syntax that is
93 | # recommended. For more details, see:
94 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
95 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
96 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
97 | config.disable_monkey_patching!
98 |
99 | # Many RSpec users commonly either run the entire suite or an individual
100 | # file, and it's useful to allow more verbose output when running an
101 | # individual spec file.
102 | if config.files_to_run.one?
103 | # Use the documentation formatter for detailed output,
104 | # unless a formatter has already been configured
105 | # (e.g. via a command-line flag).
106 | config.default_formatter = "doc"
107 | end
108 |
109 | # Print the 10 slowest examples and example groups at the
110 | # end of the spec run, to help surface which specs are running
111 | # particularly slow.
112 | config.profile_examples = 10
113 |
114 | # Run specs in random order to surface order dependencies. If you find an
115 | # order dependency and want to debug it, you can fix the order by providing
116 | # the seed, which is printed after each run.
117 | # --seed 1234
118 | config.order = :random
119 |
120 | # Seed global randomization in this process using the `--seed` CLI option.
121 | # Setting this allows you to use `--seed` to deterministically reproduce
122 | # test failures related to randomization by passing the same `--seed` value
123 | # as the one that triggered the failure.
124 | Kernel.srand config.seed
125 | =end
126 | end
127 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spgateway Rails Plugin [](https://travis-ci.org/5xRuby/spgateway-rails)
2 |
3 | This plugin provides convenient integration with [Spgateway](https://www.spgateway.com) - an online payment service in Taiwan.
4 |
5 |
6 | ## Installation
7 |
8 | Add this line to your application's Gemfile:
9 |
10 | ```ruby
11 | gem 'spgateway-rails', github: '5xRuby/spgateway-rails'
12 | ```
13 |
14 | And then execute:
15 |
16 | ```bash
17 | $ bundle
18 | ```
19 |
20 | Finally, run the install generator:
21 |
22 | ```bash
23 | $ rails generate spgateway:install
24 | ```
25 |
26 | then set your `merchant_id`, `hash_key` and `hash_iv` in `config/initializers/spgateway.rb`.
27 |
28 |
29 | ## Basic Usage
30 |
31 | ### The simplest integration: only support immediate credit card pay off
32 |
33 | 1. Place the pay button in a view, such as:
34 |
35 | ```erb
36 | <%= spgateway_pay_button 'Go pay', payment_methods: [:credit_card], order_number: @order.serial, item_description: @order.description, amount: @order.amount, payer_email: current_user&.email, class: 'btn btn-success' %>
37 | ```
38 |
39 | > Note that we restrict the supported payment methods to only `credit_card` here.
40 |
41 | 2. Configure how to process payment results in `config/initializers/spgateway.rb`, for example:
42 |
43 | ```rb
44 | config.mpg_callback do |mpg_response|
45 | if mpg_response.status == 'SUCCESS'
46 | Order.find_by(serial: mpg_response.result.merchant_order_no)
47 | .update_attributes!(paid: true)
48 | flash[:success] = mpg_response.message
49 | else
50 | flash[:error] = mpg_response.message
51 | end
52 |
53 | redirect_to orders_path
54 | end
55 | ```
56 |
57 | ### Supporting non-real-time payment methods: using `notify_callback`
58 |
59 | With some payment methods, such as ATM 轉帳 (VACC), 超商代碼繳費 (CVS) or 超商條碼繳費 (BARCODE), users will not complete their transaction on the web browser but maybe in front of an ATM or in a convenient store or so. Spgateway will notify our application later if such transactions has been done, and we will need an additional setup to deal with these notifications.
60 |
61 | > Note that you will not be able to test this intergration with an local application server (i.e. `http://localhost:3000`) directly, because (in normal cases) Spgateway cannot connect to your local computer. Consider using services like [ngrok](https://ngrok.com/) to get a public URL tunneled to your local application server, and use that public URL in the browser to get things work.
62 |
63 | 1. You'll need to setup `mpg_callback` and `notify_callback` like this:
64 |
65 | ```rb
66 | # Callback after the user has been redirect back from Spgateway MPG gateway.
67 | config.mpg_callback do |spgateway_response|
68 | # Only shows the results to the user here, while notify_callback will do the
69 | # actual work.
70 |
71 | if spgateway_response.status == 'SUCCESS'
72 | flash[:success] = spgateway_response.message
73 | else
74 | flash[:error] = spgateway_response.message
75 | end
76 |
77 | redirect_to orders_path
78 | end
79 |
80 | # Callback triggered by Spgateway after an order has been paid.
81 | config.notify_callback do |spgateway_response|
82 |
83 | if spgateway_response.status == 'SUCCESS'
84 | # Find the order and mark it as paid.
85 | Order.find_by(serial: spgateway_response.result.merchant_order_no)
86 | .update_attributes!(paid: true)
87 | else
88 | # Or log the error.
89 | Rails.logger.info "Spgateway Payment Not Succeed: #{spgateway_response.status}: #{spgateway_response.message} (#{spgateway_response.result.to_json})"
90 | end
91 | end
92 | ```
93 |
94 | The `notify_callback` will be called when Spgateway tries to notify us about payment status updates, nomatter which payment method does the user select. So in the `mpg_callback` block, we should only write code for user-facing logic, to prevent dulipaced work and unexpected results.
95 |
96 | 2. Now you can add non-real-time payment methods to your pay button:
97 |
98 | ```erb
99 | <%= spgateway_pay_button 'Go pay', payment_methods: [:credit_card, :vacc, :cvs, :barcode], order_number: @order.serial, item_description: @order.description, amount: @order.amount, payer_email: current_user&.email, class: 'btn btn-success' %>
100 | ```
101 |
102 | ### Get the customer's ATM transfer account or payment code and show them on your website with `payment_code_callback`
103 |
104 | By default, Spgateway will show the payment instruction of ATM 轉帳 (VACC), 超商代碼繳費 (CVS) or 超商條碼繳費 (BARCODE) to users on their site directly, this way you can not get the payment info and users will not be redirected back to your site.
105 |
106 | You can add the `payment_code_callback` config to let users be redirected back to your site, so then you can have the payment info and show it to your users by yourself. Do something like this:
107 |
108 | ```rb
109 | config.payment_code_callback do |spgateway_response|
110 | if spgateway_response.status == 'SUCCESS' &&
111 | spgateway_response.result.payment_type == 'VACC'
112 |
113 | bank_code = spgateway_response.result.bank_code
114 | account_number = spgateway_response.result.code_no
115 | expired_at =
116 | DateTime.parse("#{spgateway_response.result.expire_date} #{spgateway_response.result.expire_time} UTC+8")
117 | Order.find_by(serial: spgateway_response.result.merchant_order_no)
118 | .update_attributes!(bank_code: bank_code, account_number: account_number, expired_at: expired_at)
119 | flash[:info] =
120 | "Please transfer the money to bank code #{bank_code}, account number #{account_number} before #{I18n.l(expired_at)}"
121 | else
122 | Rails.logger.error "Spgateway Payment Code Receive Not Succeed: #{spgateway_response.status}: #{spgateway_response.message} (#{spgateway_response.result.to_json})"
123 | flash[:error] = "Our apologies, but an unexpected error occured, please try again"
124 | end
125 |
126 | redirect_to orders_path
127 | end
128 | ```
129 |
130 |
131 | ## TODO
132 |
133 | - Support ClientBackURL.
134 | - Build API wrapper for QueryTradeInfo.
135 | - Add option to double check the payment results after callback.
136 | - Build API wrapper for CreditCard/Cancel.
137 | - Wtite docs.
138 | - Test, test everything!
139 |
140 |
141 | ## Contributing
142 |
143 | Just open an issue or send a PR :)
144 |
145 |
146 | ## License
147 |
148 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
149 |
--------------------------------------------------------------------------------