Skip to content

Commit

Permalink
services for RSA and MD5
Browse files Browse the repository at this point in the history
  • Loading branch information
melvrickgoh committed Jun 9, 2015
1 parent 2adc0b2 commit ee3e533
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 7 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
# alipay-global
Unofficial gem for linking up to global.alipay for remote payment

Development of this gem is largely influenced by the unofficial alipay gem

## Configuration

If you're using MD5, the api_secret_key needs to be initialized. If RSA is used, the private_key_location needs to be initialized. DSA is currently not supported.

```ruby
AlipayGlobal.pid = 'YOUR_PID'
AlipayGlobal.key = 'YOUR_KEY'
AlipayGlobal.api_partner_id = 'YOUR_PID'
AlipayGlobal.api_secret_key = 'YOUR_KEY'

#AlipayGlobal.private_key_location = 'YOUR PRIVATE KEY LOCATION' #Your .pem file location

#Alipay.sign_type = 'MD5' # Available values: MD5, RSA. Default is MD5
#Alipay.debug_mode = true # Enable parameter check. Default is true.
#AlipayGlobal.sign_type = 'MD5' # Available values: MD5, RSA. Default is MD5
#AlipayGlobal.debug_mode = true # Enable parameter check. Default is true.
```
9 changes: 9 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require "bundler/gem_tasks"
require "rake/testtask"

Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = FileList['test/**/*_test.rb']
end

task :default => :test
5 changes: 3 additions & 2 deletions alipay_global.gemspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'alipay/version'
require 'alipay_global/version'

Gem::Specification.new do |spec|
spec.name = "alipay_global"
Expand All @@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
spec.email = ["melvrickgoh@kaligo.com"]
spec.description = %q{An unofficial simple global.alipay gem}
spec.summary = %q{An unofficial simple global.alipay gem}
spec.homepage = "https://github.com/melvrickgoh/alipay-global"
spec.homepage = "https://github.com/Kaligo/alipay-global.git"
spec.license = "MIT"

spec.files = `git ls-files`.split($/)
Expand All @@ -21,5 +21,6 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", "~> 1.3"
spec.add_development_dependency "rake"
spec.add_development_dependency "minitest"
spec.add_development_dependency "rspec"
spec.add_development_dependency "fakeweb"
end
6 changes: 6 additions & 0 deletions keys/alipay_public_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRA
FljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQE
B/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5Ksi
NG9zpgmLCUYuLkxpLQIDAQAB
-----END PUBLIC KEY-----
27 changes: 27 additions & 0 deletions keys/test_private_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxF1vOOysBibP9gquwCqjCigUBIGZpPsTiAyeGvt38NRwixPI
56wZgVvCMp02pVBWH7np99Qi91Axg1/A7j1z4MAqBru6XNPgn2LLIEv8xuSvJ0mI
YeB1MXZOttD2tsaNgFdU0LQsSz+HEicSfh5/1xJ5nCwQmJg/2AIGOL1igcAkqCct
cs4umwK9GFQ1FROsqz4OXUbjzByuCxmY9C5r4ALAycmjKhsy9luE1Pp9VEzT0jeJ
xfAH0vAPkGCqNv50swxJ6n+s98/ves/XmxXL3dIxAW/6RvmriNXwANsuwyn0DMsv
u1nFMZIxeCUvfwmrwkpGb5lHFBvW9pTbOCOMIwIDAQABAoIBACjRX0hO8idJNBtp
9w9dDY0T7a1OH1kLE/FI5iinszPthQNz9mQqAxc/squAwJY2j+CCbd8lSqtsEQ7E
/b7IKWlDqLjDlyaqqFuZVq38/at+Z+Zbw6zM0q+Ybx5Ta6Vsuoc+IBROD5MIvpQI
aTCsOFjFr1/GTSDbLRJCwXZT78EWiYMDnDscmvAQ/3A7c+2uQcMUAUMsPZ1gi0WY
2IdNetD9D2bwowYwoCJ82BKlciqxx2bL9ue3KnFea01pvQFisOjO69421C1baqV2
6ghT001ghrL80SvG7SNuArEGSqm+WeUC3/BoeK1/1BEsrgiXFVZZvvrMcQwWWetk
JOkJTskCgYEA6DfE2a44tdIN2spg4Wl5O/pRAA4cnpBsKCL/RYTaIXmHj5tDyqlO
oRrJuVQBesv2WDxTwaqRDA7Wbro3jtWObKHtXiiPNKw4bb2OpODdIlJXLLvRpXkq
PhiO1+0RaBX3TGyVs+JJvNWZVAj03IeMNfzjZq18fkLwDYw1PUqgNrcCgYEA2Hmv
B9Ee/SkFbheM6KYYBGNSCU6qxb0C6aaOFKrq8Ryml/3Li4Q+nFwzDiFQHPhM0m4e
mv5y/mMGR2jnkn/KwKYa3jTICp+F/ilkAop0k8yKcXrdVkb/Fb/lQw2pkqkLbKaG
/Ao20TS+hlWjAZfpvU2aVqS215PTyO6I4L9zSfUCgYEA5djtexi7ARycogbWxcaE
PR2Stx9ArKH+q+uYB9NrpN6Jk1b3Ts0uCsBdEpdXr5faiZOMw5B0aR72mDqxaytu
AZB1RlGXDWe5osWRPxljR+mAZ8Kvy72WVkgwewEnzYKQeJCxzI8atVImpcsHspBn
87gPzT3Cj6bpvD8fIz+OPRkCgYAdMINGdY9NKavexXQtpr/UT6QvNxlV4n+zC89a
wBU//9IC6qj4nhNnOBN2U02fKmgJc+nSkn7lCGs/U8jt+ydWxM8YqVtT+2Cw/dnL
cen4R/tfA+c2jAo3X5HFceEssnik5OuMrr/ng8oxCPka7OYKrZ0jE8DH3toO3QM9
8vLTxQKBgQDTQSlorlACRywudw2KE4Li7yRM9CodwBnIJPGze8N+54f3uxxc39AZ
9jT6UeK/vaiuD9VrPxF5Ze4/ZPaHctMy5O2uD4m+zPTBQo/uUGBg4m++i8b3PWfx
e8OjaXP26YiACWG/RRan8R0ZRPy8M/zLgFJUMiNrSrxchUrVXoiM4w==
-----END RSA PRIVATE KEY-----
9 changes: 9 additions & 0 deletions keys/test_public_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxF1vOOysBibP9gquwCqj
CigUBIGZpPsTiAyeGvt38NRwixPI56wZgVvCMp02pVBWH7np99Qi91Axg1/A7j1z
4MAqBru6XNPgn2LLIEv8xuSvJ0mIYeB1MXZOttD2tsaNgFdU0LQsSz+HEicSfh5/
1xJ5nCwQmJg/2AIGOL1igcAkqCctcs4umwK9GFQ1FROsqz4OXUbjzByuCxmY9C5r
4ALAycmjKhsy9luE1Pp9VEzT0jeJxfAH0vAPkGCqNv50swxJ6n+s98/ves/XmxXL
3dIxAW/6RvmriNXwANsuwyn0DMsvu1nFMZIxeCUvfwmrwkpGb5lHFBvW9pTbOCOM
IwIDAQAB
-----END PUBLIC KEY-----
6 changes: 5 additions & 1 deletion lib/alipay_global.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
require 'alipay_global/sign'
require 'alipay_global/sign/md5'
require 'alipay_global/sign/rsa'

module AlipayGlobal

@debug_mode = true
@sign_type = 'MD5'

class << self
attr_accessor :pid, :key, :sign_type, :debug_mode
attr_accessor :api_partner_id, :api_secret_key, :sign_type, :debug_mode, :private_key_location

def debug_mode?
!!@debug_mode
Expand Down
47 changes: 47 additions & 0 deletions lib/alipay_global/sign.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Alipay
module Sign
@alipay_rsa_public_key = File.read("#{File.dirname __dir__}/../keys/alipay_public_key.pem")

def self.generate(params)
params = Utils.stringify_keys(params)
sign_type = AlipayGlobal.sign_type.upcase
key = AlipayGlobal.api_secret_key
string = params_to_string(params)

case sign_type
when 'MD5'
MD5.sign(string, key)
when 'RSA'
RSA.sign(string)
when 'DSA'
DSA.sign(key, string)
else
raise ArgumentError, "invalid sign_type #{sign_type}, allow value: 'MD5', 'RSA', 'DSA'"
end
end

def self.verify?(params, options = {})
params = Utils.stringify_keys(params)

sign_type = params.delete('sign_type')
sign = params.delete('sign')
string = params_to_string(params)

case sign_type
when 'MD5'
key = options[:key] || AlipayGlobal.api_secret_key
MD5.verify?(string, key, sign)
when 'RSA'
RSA.verify?(string, @alipay_rsa_public_key, sign)
when 'DSA'
DSA.verify?(string, sign)
else
false
end
end

def self.params_to_string(params)
params.sort.map { |item| item.join('=') }.join('&')
end
end
end
16 changes: 16 additions & 0 deletions lib/alipay_global/sign/md5.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'digest/md5'

module AlipayGlobal
module Sign
class MD5
#pre-signed string should not be url encoded
def self.sign(string, secret_key)
Digest::MD5.hexdigest("#{string}#{secret_key}")
end

def self.verify?(string, secret_key, sign)
sign == sign(string, secret_key)
end
end
end
end
24 changes: 24 additions & 0 deletions lib/alipay_global/sign/rsa.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'openssl'
require 'base64'

module AlipayGlobal
module Sign
class RSA
def self.private_key
raise ArgumentError, "Assign valid location for RSA private key location :: #AlipayGlobal.private_key_location = #{AlipayGlobal.private_key_location}" if !AlipayGlobal.private_key_location
File.read(AlipayGlobal.private_key_location)
end

def self.sign(string, supplied_private_key = nil)
key = supplied_private_key || private_key
rsa = OpenSSL::PKey::RSA.new(key)
Base64.encode64(rsa.sign(OpenSSL::Digest::SHA256.new, string))
end

def self.verify?(string, public_key, sign)
rsa = OpenSSL::PKey::RSA.new(public_key)
rsa.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(sign), string)
end
end
end
end
31 changes: 31 additions & 0 deletions test/alipay_global_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'test_config'

describe "AlipayGlobal", "basic gem config" do

before do
@alipay = AlipayGlobal
end

it "has a basic debug mode default" do
assert @alipay.debug_mode?
end

it "has a default MD5 mode" do
assert_equal 'MD5', @alipay.sign_type
end

describe "api_partner_id & api_secret_key assignment" do

before do
@alipay.api_partner_id = '2088101122136241'
@alipay.api_secret_key = '760bdzec6y9goq7ctyx96ezkz78287de'
end

it "has assigned api_partner_id and api_secret_key" do
@alipay.api_partner_id.wont_be_nil true
@alipay.api_secret_key.wont_be_nil true
end

end

end
31 changes: 31 additions & 0 deletions test/sign/md5_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'test_config'

describe "AlipayGlobal::Sign::MD5", "md5 signature test" do

before do
@alipay = AlipayGlobal
@alipay.api_partner_id = '2088101122136241'
@alipay.api_secret_key = '760bdzec6y9goq7ctyx96ezkz78287de'

@params = "_input_charset=gbk&out_trade_no=6741334835157966&partner=2088101568338364&payment_type=1&return_url=http://www.test.com/alipay/return_url.asp&currency=USD&service=create_forex_trade&subject=a book&bodu=nice book&total_fee=100"

@md5_signature = "db017da14ac139afef3bb7357bb284b2"
end

describe "MD5#sign" do
it "should generate the correct signature" do
assert_equal @md5_signature, @alipay::Sign::MD5.sign(@params, @alipay.api_secret_key)
end
end

describe "MD5#verify?" do
it "should return true for the correct signature match" do
@alipay::Sign::MD5.verify?(@params, @alipay.api_secret_key, @md5_signature).must_equal true
end

it "should return false for incorrect signature match" do
@alipay::Sign::MD5.verify?(@params, @alipay.api_secret_key, "mike#{@md5_signature}").must_equal false
end
end

end
64 changes: 64 additions & 0 deletions test/sign/rsa_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require 'test_config'

describe "AlipayGlobal::Sign::RSA", "rsa signature test" do

before do
@alipay = AlipayGlobal

@params = "_input_charset=gbk&out_trade_no=6741334835157966&partner=2088101568338364&payment_type=1&return_url=http://www.test.com/alipay/return_url.asp&currency=USD&service=create_forex_trade&subject=a book&bodu=nice book&total_fee=100"

@rsa_signature = "i6GcANd+q8dTGY5kif3ClSqQbysmvPwf+gFowbB+ukKLKdcd4dlEUAWKlirK\nBviHoJwydMkUwavi5XvUieU6582UdqrlZgz1UlRSgL5NHSv7DWckhnYL7IKL\n2sTHb5derwrhjcJD/diYSnAMA0K+sRwVZ6Rs4fvQH3NN7sY4x0rb5W54QkPe\nCLqI+MzTggrcfqme2Grx19jOXSigETGWAm74CoI2lztlNgEjBpuqTmXHaBz1\nJ968XI9hVBj2mGnHU4EXj5hPSY/bFK2gVItOQ3w5RbTQCgjdpBYkM1LwTOlc\nu1/BlmWuUh82NGEK/qBrmne6z/W4GnG+6sqoAERA4g==\n"

@test_rsa_private_key = File.read("#{File.dirname __dir__}/../keys/test_private_key.pem")
@test_rsa_public_key = File.read("#{File.dirname __dir__}/../keys/test_public_key.pem")
end

describe "RSA#sign" do
describe "AlipayGlobal.private_key_location is empty" do
before do
@alipay.private_key_location = nil
end

it "should throw an Argument Error when private_key is not supplied :: #sign(params)" do
exception = proc{ (@alipay::Sign::RSA.sign(@params)).call }.must_raise(ArgumentError)
exception.message.must_equal "Assign valid location for RSA private key location :: #AlipayGlobal.private_key_location = #{@alipay.private_key_location}"
end

it "should generate the correct signature :: #sign(params, private_key)" do
assert_equal @rsa_signature, @alipay::Sign::RSA.sign(@params, @test_rsa_private_key)
end
end

describe "AlipayGlobal.private_key_location is invalid" do
before do
@alipay.private_key_location = "#{File.dirname __dir__}/../keys/test_private_key.danger.pem"
end

it "should throw an Argument Error when private_key is not supplied :: #sign(params)" do
exception = proc{ (@alipay::Sign::RSA.sign(@params)).call }.must_raise(Errno::ENOENT)
exception.message.must_include "No such file or directory"
end
end

describe "AlipayGlobal.private_key_location is filled with a valid location" do
before do
@alipay.private_key_location = "#{File.dirname __dir__}/../keys/test_private_key.pem"
end

it "should generate the correct signature :: #sign(params)" do
assert_equal @rsa_signature, @alipay::Sign::RSA.sign(@params)
end
end
end

describe "RSA#verify?" do
it "should return true for the correct signature match" do
@alipay::Sign::RSA.verify?(@params, @test_rsa_public_key, @rsa_signature).must_equal true
end

it "should return false for incorrect signature match" do
@alipay::Sign::RSA.verify?(@params, @test_rsa_public_key, "mike#{@rsa_signature}").must_equal false
end
end

end
4 changes: 4 additions & 0 deletions test/test_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require 'minitest'
require 'minitest/autorun'
require 'alipay_global'
require 'fakeweb'

0 comments on commit ee3e533

Please sign in to comment.