Copy import uuid
import logging
import requests
import http.cookiejar
from collections import namedtuple
from datetime import datetime
logger = logging.getLogger('payments')
ChargeResponse = namedtuple('ChargeResponse', [
'uid',
'amount',
'token',
'expiration',
'transaction_id',
])
RefundResponse = namedtuple('RefundResponse', [
'transaction_id',
'refunded_transaction_id',
])
class BlockAll(http.cookiejar.CookiePolicy):
def set_ok(self, cookie, request):
return False
payment_session = requests.Session()
payment_session.cookies.policy = BlockAll()
def make_payment_request(path, payload, timeout=5):
"""Make a request to the payment gateway.
path (str):
Path to post to.
payload (object):
JSON-serializable request payload.
timeout (int):
Timeout in seconds.
Raises
Unavailable
requests.exceptions.HTTPError
Returns (response)
"""
headers = {
"Authorization": "Bearer " + PAYMENT_GATEWAY_TOKEN,
}
try:
response = payment_session.post(
PAYMENT_GATEWAY_BASE_URL + path,
json=payload,
headers=headers,
timeout=timeout,
)
except (requests.ConnectionError, requests.Timeout) as e:
raise errors.Unavailable() from e
response.raise_for_status()
return response.json()
def charge(amount, token):
"""Charge credit card.
amount (int):
Amount to charge in cents.
token (str):
Credit card token.
Raises
Unavailable
Refused
Stolen
PaymentGatewayError
Returns (ChargeResponse)
"""
try:
data = make_payment_request('/charge', {
'uid': str(uuid.uuid4()),
'amount': amount,
'token': token,
})
except requests.HTTPError as e:
if e.response.status_code == 400:
error = e.response.json()
code = error['code']
message = error['message']
if code == 1:
raise Refused(code, message) from e
elif code == 2:
raise Stolen(code, message) from e
else:
raise PaymentGatewayError(code, message) from e
logger.exception("Payment service had internal error")
raise errors.Unavailable() from e
return ChargeResponse(
uid=uuid.UID(data['uid']),
amount=data['amount'],
token=data['token'],
expiration=datetime.strptime(data['expiration'], "%Y-%m-%dT%H:%M:%S.%f"),
transaction_id=data['transaction_id'],
)
def refund(transaction_id):
"""Refund charged transaction.
transaction_id (str):
Transaction id to refund.
Raises:
Return (RefundResponse)
"""
try:
data = make_payment_request('/refund', {
'uid': str(uuid.uuid4()),
'transaction_id': transaction_id,
})
except requests.HTTPError as e:
# TODO: Handle refund remote errors
return RefundResponse(
'transaction_id': data['transaction_id'],
'refunded_transaction_id': data['refunded_transaction_id'],
)