0003-add-a-method-to-guess-transaction_id-and-backend-fro.patch
eopayment/__init__.py | ||
---|---|---|
33 | 33 |
RECEIVED, |
34 | 34 |
URL, |
35 | 35 |
WAITING, |
36 |
BackendNotFound, |
|
36 | 37 |
PaymentException, |
37 | 38 |
ResponseError, |
38 | 39 |
force_text, |
... | ... | |
283 | 284 | |
284 | 285 |
def get_maximal_amount(self): |
285 | 286 |
return getattr(self.backend, 'maximal_amount', None) |
287 | ||
288 |
@property |
|
289 |
def has_guess(self): |
|
290 |
return hasattr(self.backend.__class__, 'guess') |
|
291 | ||
292 |
@classmethod |
|
293 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
294 |
'''Try to guess the type of bakcned and the transaction_id given part of an HTTP response. |
|
295 |
method CAN be GET or POST. |
|
296 |
query_string is the URL encoded query-string as-is. |
|
297 |
body is the bytes content of the response. |
|
298 |
headers can eventually give access to the response headers. |
|
299 |
backends is to limit the type of backends if the possible backends are known. |
|
300 |
''' |
|
301 | ||
302 |
for kind, backend in get_backends().items(): |
|
303 |
if not hasattr(backend, 'guess'): |
|
304 |
continue |
|
305 |
if backends and kind not in backends: |
|
306 |
continue |
|
307 |
transaction_id = backend.guess( |
|
308 |
method=method, query_string=query_string, body=body, headers=headers |
|
309 |
) |
|
310 |
if transaction_id: |
|
311 |
return kind, transaction_id |
|
312 |
raise BackendNotFound |
eopayment/common.py | ||
---|---|---|
89 | 89 |
pass |
90 | 90 | |
91 | 91 | |
92 |
class BackendNotFound(PaymentException): |
|
93 |
pass |
|
94 | ||
95 | ||
92 | 96 |
class PaymentResponse: |
93 | 97 |
"""Holds a generic view on the result of payment transaction response. |
94 | 98 |
eopayment/mollie.py | ||
---|---|---|
162 | 162 |
'%s error on endpoint "%s": %s "%s"' % (method, endpoint, e, result.get('detail', result)) |
163 | 163 |
) |
164 | 164 |
return result |
165 | ||
166 |
@classmethod |
|
167 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
168 |
for content in [query_string, body]: |
|
169 |
if isinstance(content, bytes): |
|
170 |
try: |
|
171 |
content = content.decode() |
|
172 |
except UnicodeDecodeError: |
|
173 |
pass |
|
174 |
if isinstance(content, str): |
|
175 |
fields = parse_qs(content) |
|
176 |
if set(fields) == {'id'}: |
|
177 |
return fields['id'][0] |
|
178 |
return None |
eopayment/ogone.py | ||
---|---|---|
645 | 645 |
order_id=complus or orderid, |
646 | 646 |
transaction_id=transaction_id, |
647 | 647 |
) |
648 | ||
649 |
@classmethod |
|
650 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
651 |
for content in [query_string, body]: |
|
652 |
if isinstance(content, bytes): |
|
653 |
try: |
|
654 |
content = content.decode() |
|
655 |
except UnicodeDecodeError: |
|
656 |
pass |
|
657 |
if isinstance(content, str): |
|
658 |
fields = {key.upper(): values for key, values in urlparse.parse_qs(content).items()} |
|
659 |
if not set(fields) >= {'ORDERID', 'PAYID', 'STATUS', 'NCERROR'}: |
|
660 |
continue |
|
661 |
orderid = fields.get('ORDERID') |
|
662 |
complus = fields.get('COMPLUS') |
|
663 |
if complus: |
|
664 |
return complus[0] |
|
665 |
return orderid[0] |
|
666 |
return None |
eopayment/paybox.py | ||
---|---|---|
455 | 455 | |
456 | 456 |
def cancel(self, amount, bank_data, **kwargs): |
457 | 457 |
return self.perform(amount, bank_data, PAYBOX_DIRECT_CANCEL_OPERATION) |
458 | ||
459 |
@classmethod |
|
460 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
461 |
for content in [query_string, body]: |
|
462 |
if isinstance(content, bytes): |
|
463 |
try: |
|
464 |
content = content.decode() |
|
465 |
except UnicodeDecodeError: |
|
466 |
pass |
|
467 |
if isinstance(content, str): |
|
468 |
fields = urlparse.parse_qs(content) |
|
469 |
if 'erreur' in fields and 'reference' in fields: |
|
470 |
return fields['reference'][0] |
|
471 |
return None |
eopayment/payfip_ws.py | ||
---|---|---|
374 | 374 |
test=response.saisie == 'T', |
375 | 375 |
) |
376 | 376 | |
377 |
@classmethod |
|
378 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
379 |
for content in [query_string, body]: |
|
380 |
if isinstance(content, bytes): |
|
381 |
try: |
|
382 |
content = content.decode() |
|
383 |
except UnicodeDecodeError: |
|
384 |
pass |
|
385 |
if isinstance(content, str): |
|
386 |
fields = parse_qs(content) |
|
387 |
if set(fields) == {'idOp'}: |
|
388 |
return fields['idOp'][0] |
|
389 |
return None |
|
390 | ||
377 | 391 | |
378 | 392 |
if __name__ == '__main__': |
379 | 393 |
import click |
eopayment/sips2.py | ||
---|---|---|
256 | 256 |
self.logger.debug('emitting request %r', data) |
257 | 257 |
return transactionReference, FORM, form |
258 | 258 | |
259 |
def decode_data(self, data): |
|
259 |
@classmethod |
|
260 |
def decode_data(cls, data): |
|
260 | 261 |
data = data.split('|') |
261 | 262 |
data = [map(force_text, p.split('=', 1)) for p in data] |
262 | 263 |
return collections.OrderedDict(data) |
... | ... | |
372 | 373 |
self.logger.debug('received %r', response.content) |
373 | 374 |
response.raise_for_status() |
374 | 375 |
return response.json() |
376 | ||
377 |
@classmethod |
|
378 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
379 |
for content in [query_string, body]: |
|
380 |
if isinstance(content, bytes): |
|
381 |
try: |
|
382 |
content = content.decode() |
|
383 |
except UnicodeDecodeError: |
|
384 |
pass |
|
385 |
if isinstance(content, str): |
|
386 |
fields = urlparse.parse_qs(content) |
|
387 |
if not set(fields) >= {'Data', 'Seal', 'InterfaceVersion'}: |
|
388 |
continue |
|
389 |
data = self.decode_data(fields['Data'][0]) |
|
390 |
if 'transactionReference' in data: |
|
391 |
return data['transactionReference'] |
|
392 |
return None |
eopayment/systempayv2.py | ||
---|---|---|
620 | 620 |
sign = sign_method(secret, signed_data) |
621 | 621 |
self.logger.debug('signature «%s»', sign) |
622 | 622 |
return force_text(sign) |
623 | ||
624 |
@classmethod |
|
625 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
626 |
for content in [query_string, body]: |
|
627 |
if isinstance(content, bytes): |
|
628 |
try: |
|
629 |
content = content.decode() |
|
630 |
except UnicodeDecodeError: |
|
631 |
pass |
|
632 |
if isinstance(content, str): |
|
633 |
fields = urlparse.parse_qs(content) |
|
634 |
if not set(fields) >= {SIGNATURE, VADS_CTX_MODE, VADS_AUTH_RESULT}: |
|
635 |
continue |
|
636 |
vads_eopayment_trans_id = fields.get(VADS_EOPAYMENT_TRANS_ID) |
|
637 |
vads_trans_date = fields.get(VADS_TRANS_DATE) |
|
638 |
vads_trans_id = fields.get(VADS_TRANS_ID) |
|
639 |
if vads_eopayment_trans_id: |
|
640 |
return vads_eopayment_trans_id[0] |
|
641 |
elif vads_trans_date and vads_trans_id: |
|
642 |
return vads_trans_date[0] + '_' + vads_trans_id[0] |
|
643 |
return None |
eopayment/tipi.py | ||
---|---|---|
202 | 202 |
transaction_id=refdet, |
203 | 203 |
test=test, |
204 | 204 |
) |
205 | ||
206 |
@classmethod |
|
207 |
def guess(self, *, method=None, query_string=None, body=None, headers=None, backends=(), **kwargs): |
|
208 |
for content in [query_string, body]: |
|
209 |
if isinstance(content, bytes): |
|
210 |
try: |
|
211 |
content = content.decode() |
|
212 |
except UnicodeDecodeError: |
|
213 |
pass |
|
214 |
if isinstance(content, str): |
|
215 |
fields = parse_qs(content) |
|
216 |
if 'refdet' in fields and 'resultrans' in fields: |
|
217 |
return fields['refdet'][0] |
|
218 |
return None |
tests/test_misc.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import pytest |
|
17 | 18 | |
18 | 19 |
import eopayment |
19 | 20 | |
20 | 21 | |
21 | 22 |
def test_get_backends(): |
22 | 23 |
assert len(eopayment.get_backends()) > 1 |
24 | ||
25 | ||
26 |
GUESS_TEST_VECTORS = [ |
|
27 |
{ |
|
28 |
'name': 'tipi', |
|
29 |
'kwargs': { |
|
30 |
'query_string': 'objet=tout+a+fait&montant=12312&saisie=T&mel=info%40entrouvert.com' |
|
31 |
'&numcli=12345&exer=9999&refdet=999900000000999999&resultrans=P', |
|
32 |
}, |
|
33 |
'result': ['tipi', '999900000000999999'], |
|
34 |
}, |
|
35 |
{ |
|
36 |
'name': 'payfip_ws', |
|
37 |
'kwargs': { |
|
38 |
'query_string': 'idOp=1234', |
|
39 |
}, |
|
40 |
'result': ['payfip_ws', '1234'], |
|
41 |
}, |
|
42 |
{ |
|
43 |
'name': 'systempayv2-old-transaction-id', |
|
44 |
'kwargs': { |
|
45 |
'query_string': 'vads_amount=1042&vads_auth_mode=FULL&vads_auth_number=3feadf' |
|
46 |
'&vads_auth_result=00&vads_capture_delay=0&vads_card_brand=CB' |
|
47 |
'&vads_result=00' |
|
48 |
'&vads_card_number=497010XXXXXX0000' |
|
49 |
'&vads_payment_certificate=582ba2b725057618706d7a06e9e59acdbf69ff53' |
|
50 |
'&vads_ctx_mode=TEST&vads_currency=978&vads_effective_amount=1042' |
|
51 |
'&vads_site_id=70168983&vads_trans_date=20161013101355' |
|
52 |
'&vads_trans_id=226787&vads_trans_uuid=4b5053b3b1fe4b02a07753e7a' |
|
53 |
'&vads_effective_creation_date=20200330162530' |
|
54 |
'&signature=c17fab393f94dc027dc029510c85d5fc46c4710f', |
|
55 |
}, |
|
56 |
'result': ['systempayv2', '20161013101355_226787'], |
|
57 |
}, |
|
58 |
{ |
|
59 |
'name': 'systempayv2-eo-trans-id', |
|
60 |
'kwargs': { |
|
61 |
'query_string': 'vads_amount=1042&vads_auth_mode=FULL&vads_auth_number=3feadf' |
|
62 |
'&vads_auth_result=00&vads_capture_delay=0&vads_card_brand=CB' |
|
63 |
'&vads_result=00' |
|
64 |
'&vads_card_number=497010XXXXXX0000' |
|
65 |
'&vads_payment_certificate=582ba2b725057618706d7a06e9e59acdbf69ff53' |
|
66 |
'&vads_ctx_mode=TEST&vads_currency=978&vads_effective_amount=1042' |
|
67 |
'&vads_site_id=70168983&vads_trans_date=20161013101355' |
|
68 |
'&vads_trans_id=226787&vads_trans_uuid=4b5053b3b1fe4b02a07753e7a' |
|
69 |
'&vads_effective_creation_date=20200330162530' |
|
70 |
'&signature=c17fab393f94dc027dc029510c85d5fc46c4710f' |
|
71 |
'&vads_ext_info_eopayment_trans_id=123456', |
|
72 |
}, |
|
73 |
'result': ['systempayv2', '123456'], |
|
74 |
}, |
|
75 |
{ |
|
76 |
'name': 'paybox', |
|
77 |
'kwargs': { |
|
78 |
'query_string': 'montant=4242&reference=abcdef&code_autorisation=A' |
|
79 |
'&erreur=00000&date_transaction=20200101&heure_transaction=01%3A01%3A01', |
|
80 |
}, |
|
81 |
'result': ['paybox', 'abcdef'], |
|
82 |
}, |
|
83 |
{ |
|
84 |
'name': 'ogone-no-complus', |
|
85 |
'kwargs': { |
|
86 |
'query_string': 'orderid=myorder&status=9&payid=3011229363&cn=Us%C3%A9r&ncerror=0' |
|
87 |
'&trxdate=10%2F24%2F16&acceptance=test123¤cy=eur&amount=7.5', |
|
88 |
}, |
|
89 |
'result': ['ogone', 'myorder'], |
|
90 |
}, |
|
91 |
{ |
|
92 |
'name': 'ogone-with-complus', |
|
93 |
'kwargs': { |
|
94 |
'query_string': 'complus=neworder&orderid=myorder&status=9&payid=3011229363&cn=Us%C3%A9r' |
|
95 |
'&ncerror=0&trxdate=10%2F24%2F16&acceptance=test123¤cy=eur&amount=7.5', |
|
96 |
}, |
|
97 |
'result': ['ogone', 'neworder'], |
|
98 |
}, |
|
99 |
{ |
|
100 |
'name': 'mollie', |
|
101 |
'kwargs': { |
|
102 |
'body': b'id=tr_7UhSN1zuXS', |
|
103 |
}, |
|
104 |
'result': ['mollie', 'tr_7UhSN1zuXS'], |
|
105 |
}, |
|
106 |
{ |
|
107 |
'name': 'sips2', |
|
108 |
'kwargs': { |
|
109 |
'body': ( |
|
110 |
b'Data=captureDay%3D0%7CcaptureMode%3DAUTHOR_CAPTURE%7CcurrencyCode%3D978%7CmerchantId%3D002001000000001%7CorderChannel%3D' |
|
111 |
b'INTERNET%7CresponseCode%3D00%7CtransactionDateTime%3D2016-02-01T17%3A44%3A20%2B01%3A00%7C' |
|
112 |
b'transactionReference%3D668930%7CkeyVersion%3D1%7CacquirerResponseCode%3D00%7Camou' |
|
113 |
b'nt%3D1200%7CauthorisationId%3D12345%7CcardCSCResultCode%3D4E%7CpanExpiryDate%3D201605%7Cpay' |
|
114 |
b'mentMeanBrand%3DMASTERCARD%7CpaymentMeanType%3DCARD%7CcustomerIpAddress%3D82.244.203.243%7CmaskedPan' |
|
115 |
b'%3D5100%23%23%23%23%23%23%23%23%23%23%23%2300%7CorderId%3Dd4903de7027f4d56ac01634fd7ab9526%7CholderAuthentRelegation' |
|
116 |
b'%3DN%7CholderAuthentStatus%3D3D_ERROR%7CtransactionOrigin%3DINTERNET%7CpaymentPattern%3D' |
|
117 |
b'ONE_SHOT&Seal=6ca3247765a19b45d25ad54ef4076483e7d55583166bd5ac9c64357aac097602&InterfaceVersion=HP_2.0&Encode=' |
|
118 |
), |
|
119 |
}, |
|
120 |
'result': ['sips2', '668930'], |
|
121 |
}, |
|
122 |
{ |
|
123 |
'name': 'notfound', |
|
124 |
'kwargs': {}, |
|
125 |
'exception': eopayment.BackendNotFound, |
|
126 |
}, |
|
127 |
{ |
|
128 |
'name': 'notfound-2', |
|
129 |
'kwargs': {'query_string': None, 'body': [12323], 'headers': {b'1': '2'}}, |
|
130 |
'exception': eopayment.BackendNotFound, |
|
131 |
}, |
|
132 |
{ |
|
133 |
'name': 'backends-limitation', |
|
134 |
'kwargs': { |
|
135 |
'body': b'id=tr_7UhSN1zuXS', |
|
136 |
'backends': ['payfips_ws'], |
|
137 |
}, |
|
138 |
'exception': eopayment.BackendNotFound, |
|
139 |
}, |
|
140 |
] |
|
141 | ||
142 | ||
143 |
@pytest.mark.parametrize('test_vector', GUESS_TEST_VECTORS, ids=lambda tv: tv['name']) |
|
144 |
def test_guess(test_vector): |
|
145 |
kwargs, result, exception = test_vector['kwargs'], test_vector.get('result'), test_vector.get('exception') |
|
146 |
if exception is not None: |
|
147 |
with pytest.raises(exception): |
|
148 |
eopayment.Payment.guess(**kwargs) |
|
149 |
else: |
|
150 |
assert list(eopayment.Payment.guess(**kwargs)) == result |
|
23 |
- |