webmoney 0.0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|