webmoney 0.0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +35 -0
- data/ext/wmsigner/base64.cpp +144 -0
- data/ext/wmsigner/base64.h +48 -0
- data/ext/wmsigner/cmdbase.cpp +417 -0
- data/ext/wmsigner/cmdbase.h +111 -0
- data/ext/wmsigner/crypto.cpp +155 -0
- data/ext/wmsigner/crypto.h +17 -0
- data/ext/wmsigner/extconf.rb +16 -0
- data/ext/wmsigner/md4.cpp +192 -0
- data/ext/wmsigner/md4.h +41 -0
- data/ext/wmsigner/rsalib1.cpp +838 -0
- data/ext/wmsigner/rsalib1.h +81 -0
- data/ext/wmsigner/signer.cpp +315 -0
- data/ext/wmsigner/signer.h +63 -0
- data/ext/wmsigner/stdafx.cpp +8 -0
- data/ext/wmsigner/stdafx.h +33 -0
- data/ext/wmsigner/wmsigner.cpp +119 -0
- data/lib/WebMoneyCA.crt +90 -0
- data/lib/messenger.rb +37 -0
- data/lib/passport.rb +44 -0
- data/lib/webmoney.rb +237 -0
- data/lib/wmid.rb +14 -0
- data/rakefile +63 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/messenger_spec.rb +23 -0
- data/spec/unit/passport_spec.rb +47 -0
- data/spec/unit/signer_spec.rb +35 -0
- data/spec/unit/time_spec.rb +14 -0
- data/spec/unit/webmoney_spec.rb +111 -0
- data/spec/unit/wmid_spec.rb +30 -0
- data/tools/rakehelp.rb +117 -0
- metadata +102 -0
@@ -0,0 +1,8 @@
|
|
1
|
+
// stdafx.cpp : source file that includes just the standard includes
|
2
|
+
// wmsigner.pch will be the pre-compiled header
|
3
|
+
// stdafx.obj will contain the pre-compiled type information
|
4
|
+
|
5
|
+
#include "stdafx.h"
|
6
|
+
|
7
|
+
// TODO: reference any additional headers you need in STDAFX.H
|
8
|
+
// and not in this file
|
@@ -0,0 +1,33 @@
|
|
1
|
+
// stdafx.h : include file for standard system include files,
|
2
|
+
// or project specific include files that are used frequently, but
|
3
|
+
// are changed infrequently
|
4
|
+
//
|
5
|
+
|
6
|
+
// REMOVE COMMENT FOR DEBUG MODE
|
7
|
+
//#ifndef _DEBUG
|
8
|
+
//#define _DEBUG
|
9
|
+
//#endif
|
10
|
+
|
11
|
+
|
12
|
+
#include <stdio.h>
|
13
|
+
#include <stdlib.h>
|
14
|
+
#include <string.h>
|
15
|
+
#include <sys/types.h>
|
16
|
+
#include <errno.h>
|
17
|
+
#ifdef _WIN32
|
18
|
+
#pragma once
|
19
|
+
#include <iostream>
|
20
|
+
#include <tchar.h>
|
21
|
+
#include <windows.h>
|
22
|
+
#include <io.h>
|
23
|
+
#include <memory.h>
|
24
|
+
#pragma warning(disable : 4996)
|
25
|
+
#endif
|
26
|
+
// TODO: reference additional headers your program requires here
|
27
|
+
#ifndef TRUE
|
28
|
+
#define TRUE 1
|
29
|
+
#endif
|
30
|
+
#ifndef FALSE
|
31
|
+
#define FALSE 0
|
32
|
+
#endif
|
33
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
// Include the Ruby headers and goodies
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <ctype.h>
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "signer.h"
|
6
|
+
#include "base64.h"
|
7
|
+
#include "cmdbase.h"
|
8
|
+
|
9
|
+
#define ASCII_SIZE 512
|
10
|
+
#define BUF_64_SIZE 220
|
11
|
+
|
12
|
+
// Defining a space for information and references about the module to be stored internally
|
13
|
+
VALUE cSigner;
|
14
|
+
|
15
|
+
typedef VALUE (ruby_method)(...);
|
16
|
+
|
17
|
+
// Prototype for the initialization method - Ruby calls this, not you
|
18
|
+
//extern "C" void Init_Wmutils();
|
19
|
+
|
20
|
+
// Prototype for our method 'test1' - methods are prefixed by 'method_' here
|
21
|
+
// typedef VALUE method_test1(VALUE self);
|
22
|
+
// typedef VALUE initialize(VALUE self, const char *szWMID, const char *szPwd, const char *szKeyData);
|
23
|
+
|
24
|
+
//Signer *pSign;
|
25
|
+
|
26
|
+
void signer_free(Signer *p)
|
27
|
+
{
|
28
|
+
if (p)
|
29
|
+
delete p;
|
30
|
+
}
|
31
|
+
|
32
|
+
bool IsWmid(const char* sz)
|
33
|
+
{
|
34
|
+
int len = strlen(sz);
|
35
|
+
if (len != 12) return false;
|
36
|
+
for(int i = 0; i < len; i++)
|
37
|
+
{
|
38
|
+
if (!isdigit(sz[i]))
|
39
|
+
return false;
|
40
|
+
}
|
41
|
+
return true;
|
42
|
+
}
|
43
|
+
|
44
|
+
extern "C" VALUE signer_new(VALUE self, VALUE szWMID, VALUE szPwd, VALUE szKeyData64)
|
45
|
+
{
|
46
|
+
Signer *pSign;
|
47
|
+
|
48
|
+
if(NIL_P(szWMID)) rb_raise(rb_eArgError, "nil wmid");
|
49
|
+
|
50
|
+
// check WMID
|
51
|
+
if (! IsWmid(RSTRING(szWMID)->ptr)) rb_raise(rb_eArgError, "Incorrect WMID");
|
52
|
+
|
53
|
+
if(NIL_P(szPwd)) rb_raise(rb_eArgError, "nil password");
|
54
|
+
|
55
|
+
if(NIL_P(szKeyData64)) rb_raise(rb_eArgError, "nil key");
|
56
|
+
|
57
|
+
// check base64 data
|
58
|
+
if ( RSTRING(szKeyData64)->len != 220 ) rb_raise(rb_eArgError, "Illegal size for base64 keydata");
|
59
|
+
|
60
|
+
char KeyBuffer[ASCII_SIZE];
|
61
|
+
int bytes = code64( ENCODE, KeyBuffer, ASCII_SIZE, RSTRING(szKeyData64)->ptr, BUF_64_SIZE );
|
62
|
+
|
63
|
+
// check encoded key
|
64
|
+
if ( bytes != 164) rb_raise(rb_eArgError, "Illegal size for keydata");
|
65
|
+
|
66
|
+
pSign = new Signer(RSTRING(szWMID)->ptr, RSTRING(szPwd)->ptr, "");
|
67
|
+
VALUE tdata = Data_Wrap_Struct(self, 0, signer_free, pSign);
|
68
|
+
|
69
|
+
pSign->isIgnoreKeyFile = TRUE;
|
70
|
+
pSign->Key64Flag = TRUE;
|
71
|
+
|
72
|
+
if (pSign) pSign->SetKeyFromCL( TRUE, KeyBuffer );
|
73
|
+
|
74
|
+
return tdata;
|
75
|
+
}
|
76
|
+
|
77
|
+
extern "C" VALUE signer_init(VALUE self)
|
78
|
+
{
|
79
|
+
return self;
|
80
|
+
}
|
81
|
+
|
82
|
+
extern "C" VALUE signer_sign(VALUE self, VALUE szIn)
|
83
|
+
{
|
84
|
+
VALUE ret;
|
85
|
+
ret = rb_str_new2("");
|
86
|
+
|
87
|
+
Signer *pSign;
|
88
|
+
|
89
|
+
Data_Get_Struct(self, Signer, pSign);
|
90
|
+
|
91
|
+
if(NIL_P(szIn)) rb_raise(rb_eArgError, "nil for sign");
|
92
|
+
|
93
|
+
if (pSign)
|
94
|
+
{
|
95
|
+
szptr szSign;
|
96
|
+
if (pSign->Sign(RSTRING(szIn)->ptr, szSign))
|
97
|
+
{
|
98
|
+
ret = rb_str_new2((char *)(const char *)szSign);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
int err_no = pSign->ErrorCode();
|
103
|
+
if (err_no){
|
104
|
+
char err[20];
|
105
|
+
sprintf(err, "Signer error: %d", err_no);
|
106
|
+
rb_raise(rb_eStandardError, err);
|
107
|
+
}
|
108
|
+
|
109
|
+
return ret;
|
110
|
+
}
|
111
|
+
|
112
|
+
// The initialization method for this module
|
113
|
+
extern "C" void Init_wmsigner()
|
114
|
+
{
|
115
|
+
cSigner = rb_define_class("Signer", rb_cObject);
|
116
|
+
rb_define_singleton_method(cSigner, "new", (ruby_method*) &signer_new, 3);
|
117
|
+
rb_define_method(cSigner, "initialize", (ruby_method*) &signer_init, 0);
|
118
|
+
rb_define_method(cSigner, "sign", (ruby_method*) &signer_sign, 1);
|
119
|
+
}
|
data/lib/WebMoneyCA.crt
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
WebMoney Transfer Root Authority
|
2
|
+
================================
|
3
|
+
SHA1 Fingerprint: 76:7C:A8:69:09:10:F0:69:32:33:66:A9:C4:5D:78:6B:D1:37:17:16
|
4
|
+
PEM Data:
|
5
|
+
-----BEGIN CERTIFICATE-----
|
6
|
+
MIIEGTCCAwGgAwIBAgIQNHU6UsCDcLJD7Qw+0yqyRjANBgkqhkiG9w0BAQUFADBv
|
7
|
+
MRowGAYDVQQKExFXZWJNb25leSBUcmFuc2ZlcjEmMCQGA1UECxMdV2ViTW9uZXkg
|
8
|
+
Q2VydGlmaWNhdGlvbiBDZW50ZXIxKTAnBgNVBAMTIFdlYk1vbmV5IFRyYW5zZmVy
|
9
|
+
IFJvb3QgQXV0aG9yaXR5MB4XDTAxMDQwNDEzNDcwOVoXDTEwMDYwNzE5NDcxOFow
|
10
|
+
bzEaMBgGA1UEChMRV2ViTW9uZXkgVHJhbnNmZXIxJjAkBgNVBAsTHVdlYk1vbmV5
|
11
|
+
IENlcnRpZmljYXRpb24gQ2VudGVyMSkwJwYDVQQDEyBXZWJNb25leSBUcmFuc2Zl
|
12
|
+
ciBSb290IEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
13
|
+
AO9V0RYrJYPCOR7odiyICWNuoZj0tN9Z1mY10s+QIYGsMZCNG0meQymt5jTuhD/E
|
14
|
+
GNAHVVR9st3C92pqy2UAvQyAxMmzg5SJbuo/39YSAqx0vEtehl4QfHWHSiFStbbt
|
15
|
+
ZslHdl1jYKLwm6B88XI3OpiAoMAccntSazvim1k18lZ2bsTJeiH4s8BLP5T0Yw+R
|
16
|
+
belHaJe1DiRWChoKqoVqsDp5mH7v0cEpD78KltHo57zUuH+rzTcTeCKkO4/9CWiF
|
17
|
+
qY2FzC09wekFJ8LZ0HMlcANdLYSvmz3Ux3BvFqy8Jyt8dBhq8MRUkzZC8Cg5/lfs
|
18
|
+
ZSMHHsr1PnsIuM0OGbb1GJMCAwEAAaOBsDCBrTALBgNVHQ8EBAMCAcYwDwYDVR0T
|
19
|
+
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUy51+UX4IIMT1cJShzlLHBU0/M/MwXAYDVR0f
|
20
|
+
BFUwUzBRoE+gTYZLaHR0cDovL3d3dy53bWNlcnQuY29tL0NlcnRFbnJvbGwvV2Vi
|
21
|
+
TW9uZXklMjBUcmFuc2ZlciUyMFJvb3QlMjBBdXRob3JpdHkuY3JsMBAGCSsGAQQB
|
22
|
+
gjcVAQQDAgECMA0GCSqGSIb3DQEBBQUAA4IBAQBqGyv5FVpV4MK0VYXPFqUhsyVc
|
23
|
+
SDqM+hu4U0OA0cIKHOXRV6BcbDzsNi1+6HVA2vr9PIcIVTRlGXcU95JcQe3tjbXQ
|
24
|
+
NNMpKcgL0YMW0ifMA2qu5y6nJTIu7rKCESUtsMzQUnccaXmYn0DChYuvPObb5suf
|
25
|
+
RVlsHL9Gc90eRiR8lofaDqIM82vETsQEIj1M1ifUgD5LxZuwQ8fdESZ/GHkxCosa
|
26
|
+
9YG7WZKTMEgPSXpLmd+y24NklMi9cb+0dJce84OgMUfFAxYO71lkc0dJ5B7TJsqS
|
27
|
+
Ymwnj0x4w32Av3TiEJOQMghAJVsdPJiEzUiHMVDsAbyXQ03gZIyexFpl4QDy
|
28
|
+
-----END CERTIFICATE-----
|
29
|
+
Certificate Ingredients:
|
30
|
+
Data:
|
31
|
+
Version: 3 (0x2)
|
32
|
+
Serial Number:
|
33
|
+
34:75:3a:52:c0:83:70:b2:43:ed:0c:3e:d3:2a:b2:46
|
34
|
+
Signature Algorithm: sha1WithRSAEncryption
|
35
|
+
Issuer: O=WebMoney Transfer, OU=WebMoney Certification Center, CN=WebMoney Transfer Root Authority
|
36
|
+
Validity
|
37
|
+
Not Before: Apr 4 13:47:09 2001 GMT
|
38
|
+
Not After : Jun 7 19:47:18 2010 GMT
|
39
|
+
Subject: O=WebMoney Transfer, OU=WebMoney Certification Center, CN=WebMoney Transfer Root Authority
|
40
|
+
Subject Public Key Info:
|
41
|
+
Public Key Algorithm: rsaEncryption
|
42
|
+
RSA Public Key: (2048 bit)
|
43
|
+
Modulus (2048 bit):
|
44
|
+
00:ef:55:d1:16:2b:25:83:c2:39:1e:e8:76:2c:88:
|
45
|
+
09:63:6e:a1:98:f4:b4:df:59:d6:66:35:d2:cf:90:
|
46
|
+
21:81:ac:31:90:8d:1b:49:9e:43:29:ad:e6:34:ee:
|
47
|
+
84:3f:c4:18:d0:07:55:54:7d:b2:dd:c2:f7:6a:6a:
|
48
|
+
cb:65:00:bd:0c:80:c4:c9:b3:83:94:89:6e:ea:3f:
|
49
|
+
df:d6:12:02:ac:74:bc:4b:5e:86:5e:10:7c:75:87:
|
50
|
+
4a:21:52:b5:b6:ed:66:c9:47:76:5d:63:60:a2:f0:
|
51
|
+
9b:a0:7c:f1:72:37:3a:98:80:a0:c0:1c:72:7b:52:
|
52
|
+
6b:3b:e2:9b:59:35:f2:56:76:6e:c4:c9:7a:21:f8:
|
53
|
+
b3:c0:4b:3f:94:f4:63:0f:91:6d:e9:47:68:97:b5:
|
54
|
+
0e:24:56:0a:1a:0a:aa:85:6a:b0:3a:79:98:7e:ef:
|
55
|
+
d1:c1:29:0f:bf:0a:96:d1:e8:e7:bc:d4:b8:7f:ab:
|
56
|
+
cd:37:13:78:22:a4:3b:8f:fd:09:68:85:a9:8d:85:
|
57
|
+
cc:2d:3d:c1:e9:05:27:c2:d9:d0:73:25:70:03:5d:
|
58
|
+
2d:84:af:9b:3d:d4:c7:70:6f:16:ac:bc:27:2b:7c:
|
59
|
+
74:18:6a:f0:c4:54:93:36:42:f0:28:39:fe:57:ec:
|
60
|
+
65:23:07:1e:ca:f5:3e:7b:08:b8:cd:0e:19:b6:f5:
|
61
|
+
18:93
|
62
|
+
Exponent: 65537 (0x10001)
|
63
|
+
X509v3 extensions:
|
64
|
+
X509v3 Key Usage:
|
65
|
+
Digital Signature, Non Repudiation, Certificate Sign, CRL Sign
|
66
|
+
X509v3 Basic Constraints: critical
|
67
|
+
CA:TRUE
|
68
|
+
X509v3 Subject Key Identifier:
|
69
|
+
CB:9D:7E:51:7E:08:20:C4:F5:70:94:A1:CE:52:C7:05:4D:3F:33:F3
|
70
|
+
X509v3 CRL Distribution Points:
|
71
|
+
URI:http://www.wmcert.com/CertEnroll/WebMoney%20Transfer%20Root%20Authority.crl
|
72
|
+
|
73
|
+
1.3.6.1.4.1.311.21.1:
|
74
|
+
...
|
75
|
+
Signature Algorithm: sha1WithRSAEncryption
|
76
|
+
6a:1b:2b:f9:15:5a:55:e0:c2:b4:55:85:cf:16:a5:21:b3:25:
|
77
|
+
5c:48:3a:8c:fa:1b:b8:53:43:80:d1:c2:0a:1c:e5:d1:57:a0:
|
78
|
+
5c:6c:3c:ec:36:2d:7e:e8:75:40:da:fa:fd:3c:87:08:55:34:
|
79
|
+
65:19:77:14:f7:92:5c:41:ed:ed:8d:b5:d0:34:d3:29:29:c8:
|
80
|
+
0b:d1:83:16:d2:27:cc:03:6a:ae:e7:2e:a7:25:32:2e:ee:b2:
|
81
|
+
82:11:25:2d:b0:cc:d0:52:77:1c:69:79:98:9f:40:c2:85:8b:
|
82
|
+
af:3c:e6:db:e6:cb:9f:45:59:6c:1c:bf:46:73:dd:1e:46:24:
|
83
|
+
7c:96:87:da:0e:a2:0c:f3:6b:c4:4e:c4:04:22:3d:4c:d6:27:
|
84
|
+
d4:80:3e:4b:c5:9b:b0:43:c7:dd:11:26:7f:18:79:31:0a:8b:
|
85
|
+
1a:f5:81:bb:59:92:93:30:48:0f:49:7a:4b:99:df:b2:db:83:
|
86
|
+
64:94:c8:bd:71:bf:b4:74:97:1e:f3:83:a0:31:47:c5:03:16:
|
87
|
+
0e:ef:59:64:73:47:49:e4:1e:d3:26:ca:92:62:6c:27:8f:4c:
|
88
|
+
78:c3:7d:80:bf:74:e2:10:93:90:32:08:40:25:5b:1d:3c:98:
|
89
|
+
84:cd:48:87:31:50:ec:01:bc:97:43:4d:e0:64:8c:9e:c4:5a:
|
90
|
+
65:e1:00:f2
|
data/lib/messenger.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'iconv'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
class Webmoney
|
5
|
+
|
6
|
+
class Messenger
|
7
|
+
|
8
|
+
def initialize(owner)
|
9
|
+
@webmoney = owner
|
10
|
+
@queue = Queue.new
|
11
|
+
@thread = Thread.new(@queue) do |q|
|
12
|
+
loop do
|
13
|
+
msg = q.pop
|
14
|
+
unless msg.nil?
|
15
|
+
begin
|
16
|
+
result = @webmoney.request(:send_message, msg)
|
17
|
+
@queue.push(msg) unless result.kind_of?(Hash)
|
18
|
+
rescue ResultError
|
19
|
+
puts "ResultError: #{@webmoney.error} #{@webmoney.errormsg}"
|
20
|
+
# Silent drop message
|
21
|
+
rescue ResponseError
|
22
|
+
puts "ResponseError: #{@webmoney.error}"
|
23
|
+
# Requeue message
|
24
|
+
@queue.push(msg)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def push(msg)
|
32
|
+
@queue.push(msg)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/passport.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
|
3
|
+
# Class for store attestat information
|
4
|
+
class Webmoney
|
5
|
+
class Passport < Wmid
|
6
|
+
|
7
|
+
# doc - Hpricot::Doc or xml-string
|
8
|
+
def initialize(doc)
|
9
|
+
doc = Hpricot.XML(doc) unless doc.kind_of?(Hpricot::Doc)
|
10
|
+
root = doc.at('/response')
|
11
|
+
if root && root['retval'] && root['retval'].to_i == 0
|
12
|
+
super(doc.at('/response/certinfo')['wmid'])
|
13
|
+
tid = doc.at('/response/certinfo/attestat/row')['tid'].to_i
|
14
|
+
recalled = doc.at('/response/certinfo/attestat/row')['recalled'].to_i
|
15
|
+
locked = doc.at('/response/certinfo/userinfo/value/row')['locked'].to_i
|
16
|
+
@attestat = ( recalled + locked > 0) ? ALIAS : tid
|
17
|
+
@created_at = Time.xmlschema(doc.at('/response/certinfo/attestat/row')['datecrt'])
|
18
|
+
|
19
|
+
# TODO more attestat fields...
|
20
|
+
|
21
|
+
# Make all instance variables readable
|
22
|
+
instance_variables.each do |n|
|
23
|
+
class << self; self; end.instance_eval {
|
24
|
+
attr_reader n.sub(/^@/, '')
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Attestate types
|
31
|
+
ALIAS = 100
|
32
|
+
FORMAL = 110
|
33
|
+
START = 120
|
34
|
+
PERSONAL = 130
|
35
|
+
PAYER = 135
|
36
|
+
CAPITALLER = 136
|
37
|
+
DEVELOPER = 140
|
38
|
+
REGISTRATOR = 150
|
39
|
+
GARANT = 170
|
40
|
+
SERVICE = 190
|
41
|
+
SERVICE2 = 200
|
42
|
+
OPERATOR = 300
|
43
|
+
end
|
44
|
+
end
|
data/lib/webmoney.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
require File.dirname(__FILE__) +'/wmsigner'
|
2
|
+
require 'time'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'iconv'
|
7
|
+
require 'builder'
|
8
|
+
require 'hpricot'
|
9
|
+
|
10
|
+
class Object
|
11
|
+
def blank?
|
12
|
+
respond_to?(:empty?) ? empty? : !self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Main class for Webmoney lib. Instance contain info
|
17
|
+
# for Webmoney interfaces requests (wmid, key, etc).
|
18
|
+
# Implement base requests to Webmoney.
|
19
|
+
class Webmoney
|
20
|
+
|
21
|
+
# Error classes
|
22
|
+
class WebmoneyError < StandardError; end
|
23
|
+
class RequestError < WebmoneyError; end
|
24
|
+
class ResultError < WebmoneyError; end
|
25
|
+
class IncorrectWmidError < WebmoneyError; end
|
26
|
+
class CaCertificateError < WebmoneyError; end
|
27
|
+
|
28
|
+
require File.dirname(__FILE__) + '/../lib/wmid'
|
29
|
+
require File.dirname(__FILE__) + '/../lib/passport'
|
30
|
+
require File.dirname(__FILE__) + '/../lib/messenger'
|
31
|
+
|
32
|
+
attr_reader :wmid, :error, :errormsg, :last_request, :messenger
|
33
|
+
|
34
|
+
# Required options:
|
35
|
+
# :wmid (WMID)
|
36
|
+
# :password (on Classic key or Light X509 certtificate & key)
|
37
|
+
# :key (Base64 string for Classic key)
|
38
|
+
# OR TODO!
|
39
|
+
# :key (OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object) AND
|
40
|
+
# :cert OpenSSL::X509::Certificate object
|
41
|
+
# Optional:
|
42
|
+
# :ca_cert (path of a CA certification file in PEM format)
|
43
|
+
def initialize(opt = {})
|
44
|
+
@wmid = Wmid.new(opt[:wmid])
|
45
|
+
|
46
|
+
# classic or light
|
47
|
+
case opt[:key]
|
48
|
+
when String
|
49
|
+
@signer = Signer.new(@wmid, opt[:password], opt[:key])
|
50
|
+
when OpenSSL::PKey::RSA, OpenSSL::PKey::DSA
|
51
|
+
@key = opt[:key]
|
52
|
+
@cert = opt[:cert]
|
53
|
+
@password = opt[:password]
|
54
|
+
end
|
55
|
+
|
56
|
+
# ca_cert or default
|
57
|
+
@ca_cert =
|
58
|
+
if opt[:ca_cert].nil?
|
59
|
+
File.dirname(__FILE__) + '/../lib/WebMoneyCA.crt'
|
60
|
+
else
|
61
|
+
opt[:ca_cert]
|
62
|
+
end
|
63
|
+
|
64
|
+
w3s = 'https://w3s.wmtransfer.com/asp/'
|
65
|
+
|
66
|
+
@interfaces = {
|
67
|
+
'create_invoice' => URI.parse( w3s + 'XMLInvoice.asp' ), # x1
|
68
|
+
'create_transaction' => URI.parse( w3s + 'XMLTrans.asp' ), # x2
|
69
|
+
'operation_history' => URI.parse( w3s + 'XMLOperations.asp' ), # x3
|
70
|
+
'outgoing_invoices' => URI.parse( w3s + 'XMLOutInvoices.asp' ), # x4
|
71
|
+
'finish_protect' => URI.parse( w3s + 'XMLFinishProtect.asp' ), # x5
|
72
|
+
'send_message' => URI.parse( w3s + 'XMLSendMsg.asp'), # x6
|
73
|
+
'check_sign' => URI.parse( w3s + 'XMLClassicAuth.asp'), # x7
|
74
|
+
'find_wm' => URI.parse( w3s + 'XMLFindWMPurse.asp'), # x8
|
75
|
+
'balance' => URI.parse( w3s + 'XMLPurses.asp'), # x9
|
76
|
+
'incoming_invoices' => URI.parse( w3s + 'XMLInInvoices.asp'), # x10
|
77
|
+
'get_passport' => URI.parse( 'https://passport.webmoney.ru/asp/XMLGetWMPassport.asp'), # x11
|
78
|
+
'reject_protection' => URI.parse( w3s + 'XMLRejectProtect.asp'), # x13
|
79
|
+
'transaction_moneyback' => URI.parse( w3s + 'XMLTransMoneyback.asp'), # 14
|
80
|
+
'i_trust' => URI.parse( w3s + 'XMLTrustList.asp'), # x15
|
81
|
+
'trust_me' => URI.parse( w3s + 'XMLTrustList2.asp'), # x15
|
82
|
+
'trust_save' => URI.parse( w3s + 'XMLTrustSave2.asp'), # x15
|
83
|
+
'create_purse' => URI.parse( w3s + 'XMLCreatePurse.asp'), # x16
|
84
|
+
'bussines_level' => URI.parse( 'https://stats.wmtransfer.com/levels/XMLWMIDLevel.aspx')
|
85
|
+
}
|
86
|
+
# Iconv.new(to, from)
|
87
|
+
@ic_in = Iconv.new('UTF-8', 'CP1251')
|
88
|
+
@ic_out = Iconv.new('CP1251', 'UTF-8')
|
89
|
+
end
|
90
|
+
|
91
|
+
def classic?
|
92
|
+
! @signer.nil?
|
93
|
+
end
|
94
|
+
|
95
|
+
# Send message through Queue and Thread
|
96
|
+
# Params - :wmid, :subj, :text
|
97
|
+
def send_message(params)
|
98
|
+
@messenger = Messenger.new(self) if @messenger.nil?
|
99
|
+
@messenger.push(params)
|
100
|
+
end
|
101
|
+
|
102
|
+
# ================================================
|
103
|
+
# Main interface function
|
104
|
+
# ================================================
|
105
|
+
def request(iface, opt ={})
|
106
|
+
reqn = reqn()
|
107
|
+
raise ArgumentError unless opt.kind_of?(Hash)
|
108
|
+
opt[:wmid] = @wmid if opt[:wmid].nil?
|
109
|
+
x = Builder::XmlMarkup.new(:indent => 1)
|
110
|
+
x.instruct!(:xml, :version=>"1.0", :encoding=>"windows-1251")
|
111
|
+
unless [:get_passport, :bussines_level].include?(iface)
|
112
|
+
x.tag!('w3s.request') do
|
113
|
+
x.wmid @wmid
|
114
|
+
case iface
|
115
|
+
when :check_sign then
|
116
|
+
plan_out = @ic_out.iconv(opt[:plan])
|
117
|
+
x.testsign do
|
118
|
+
x.wmid opt[:wmid]
|
119
|
+
x.plan { x.cdata! plan_out }
|
120
|
+
x.sign opt[:sign]
|
121
|
+
end
|
122
|
+
plan = @wmid + opt[:wmid] + plan_out + opt[:sign]
|
123
|
+
when :send_message
|
124
|
+
x.reqn reqn
|
125
|
+
msgsubj = @ic_out.iconv(opt[:subj])
|
126
|
+
msgtext = @ic_out.iconv(opt[:text])
|
127
|
+
x.message do
|
128
|
+
x.receiverwmid opt[:wmid]
|
129
|
+
x.msgsubj { x.cdata! msgsubj }
|
130
|
+
x.msgtext { x.cdata! msgtext }
|
131
|
+
end
|
132
|
+
plan = opt[:wmid] + reqn + msgtext + msgsubj
|
133
|
+
end
|
134
|
+
x.sign sign(plan) if classic?
|
135
|
+
end
|
136
|
+
else
|
137
|
+
case iface
|
138
|
+
when :bussines_level
|
139
|
+
x.tag!('WMIDLevel.request') do
|
140
|
+
x.signerwmid @wmid
|
141
|
+
x.wmid opt[:wmid]
|
142
|
+
end
|
143
|
+
when :get_passport
|
144
|
+
x.request do
|
145
|
+
x.wmid @wmid
|
146
|
+
x.passportwmid opt[:wmid]
|
147
|
+
x.params { x.dict 0; x.info 1; x.mode 0 }
|
148
|
+
x.sign sign(@wmid + opt[:wmid]) if classic?
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Request do!
|
154
|
+
res = https_request(iface, x.target!)
|
155
|
+
|
156
|
+
# Parse response
|
157
|
+
parse_retval(res)
|
158
|
+
doc = Hpricot.XML(res)
|
159
|
+
case iface
|
160
|
+
when :check_sign
|
161
|
+
return doc.at('//testsign/res').inner_html == 'yes' ? true : false
|
162
|
+
when :get_passport
|
163
|
+
return Passport.new(doc)
|
164
|
+
when :bussines_level
|
165
|
+
return doc.at('//level').inner_html.to_i
|
166
|
+
when :send_message
|
167
|
+
time = doc.at('//message/datecrt').inner_html
|
168
|
+
m = time.match(/(\d{4})(\d{2})(\d{2}) (\d{2}):(\d{2}):(\d{2})/)
|
169
|
+
time = Time.mktime(*m[1..6])
|
170
|
+
return {:id => doc.at('//message')['id'], :date => time}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
protected
|
175
|
+
|
176
|
+
# Signing string by instance wmid's
|
177
|
+
# Return signed string
|
178
|
+
def sign(str)
|
179
|
+
@signer.sign(str) unless str.blank?
|
180
|
+
end
|
181
|
+
|
182
|
+
# Make HTTPS request, return result body if 200 OK
|
183
|
+
def https_request(iface, xml)
|
184
|
+
url = case iface
|
185
|
+
when Symbol
|
186
|
+
@interfaces[iface.to_s]
|
187
|
+
when String
|
188
|
+
URI.parse(iface)
|
189
|
+
end
|
190
|
+
http = Net::HTTP.new(url.host, url.port)
|
191
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
192
|
+
if File.file? @ca_cert
|
193
|
+
http.ca_file = @ca_cert
|
194
|
+
else
|
195
|
+
raise CaCertificateError
|
196
|
+
end
|
197
|
+
http.use_ssl = true
|
198
|
+
@last_request = xml
|
199
|
+
result = http.post( url.path, xml, "Content-Type" => "text/xml" )
|
200
|
+
case result
|
201
|
+
when Net::HTTPSuccess
|
202
|
+
# replace root tag for Hpricot
|
203
|
+
res = result.body.gsub(/(w3s\.response|WMIDLevel\.response)/,'w3s_response')
|
204
|
+
return @ic_in.iconv(res)
|
205
|
+
else
|
206
|
+
@error = result.code
|
207
|
+
@errormsg = result.body if result.class.body_permitted?()
|
208
|
+
raise RequestError
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def parse_retval(response_xml)
|
213
|
+
doc = Hpricot.XML(response_xml)
|
214
|
+
retval_element = doc.at('//retval')
|
215
|
+
# Workaround for passport interface
|
216
|
+
unless retval_element.nil?
|
217
|
+
retval = retval_element.inner_html.to_i
|
218
|
+
retdesc = doc.at('//retdesc').inner_html unless doc.at('//retdesc').nil?
|
219
|
+
else
|
220
|
+
retval = doc.at('//response')['retval'].to_i
|
221
|
+
retdesc = doc.at('//response')['retdesc']
|
222
|
+
end
|
223
|
+
unless retval == 0
|
224
|
+
@error = retval
|
225
|
+
@errormsg = retdesc
|
226
|
+
raise ResultError
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Create unique Request Number based on time
|
231
|
+
# Return 16 digits string
|
232
|
+
def reqn
|
233
|
+
t = Time.now
|
234
|
+
t.strftime('%Y%m%d%H%M%S') + t.to_f.to_s.match(/\.(\d\d)/)[1]
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|