def _transaction_action(transaction: Transaction, amount: int, action: TransactionAction) -> Tuple[bool, str]: version = '2.0' shop_id = resolve(settings.WS_PAY_SHOP_ID) secret_key = resolve(settings.WS_PAY_SECRET_KEY) signature = generate_signature([ shop_id, transaction.ws_pay_order_id, secret_key, transaction.stan, secret_key, transaction.approval_code, secret_key, str(amount), secret_key, transaction.ws_pay_order_id ]) data = { 'Version': version, 'WsPayOrderId': transaction.ws_pay_order_id, 'ShopID': shop_id, 'ApprovalCode': transaction.approval_code, 'STAN': transaction.stan, 'Amount': amount, 'Signature': signature } r = requests.post(f'{get_services_endpoint()}/{action.value}', data=data) response_data = r.json() expected_signature = generate_signature([ shop_id, secret_key, response_data['STAN'], response_data['ActionSuccess'], secret_key, response_data['ApprovalCode'], response_data['WsPayOrderId'], ]) if response_data['Signature'] != expected_signature: raise ValidationError('Bad signature') # Per WSPay docs update approval_code in case it was changed new_approval_code = response_data['ApprovalCode'] if transaction.approval_code != new_approval_code: transaction.approval_code = new_approval_code transaction.save() success = bool(int(response_data['ActionSuccess'])) error_message = response_data.get('ErrorMessage', '') return success, error_message
def test_wspay_encode(): """Test the processing function which prepares the data for WSPay.""" shop_id = resolve(settings.WS_PAY_SHOP_ID) secret_key = resolve(settings.WS_PAY_SECRET_KEY) assert shop_id == 'MojShop' assert secret_key == '3DfEO2B5Jjm4VC1Q3vEh' return_data = { 'ShopID': shop_id, 'Version': resolve(settings.WS_PAY_VERSION), 'TotalAmount': '10,00', 'ReturnURL': ( 'http://testserver' + reverse('wspay:process-response', kwargs={'status': 'success'}) ), 'CancelURL': ( 'http://testserver' + reverse('wspay:process-response', kwargs={'status': 'cancel'}) ), 'ReturnErrorURL': ( 'http://testserver' + reverse('wspay:process-response', kwargs={'status': 'error'}) ), 'ReturnMethod': 'POST', } incoming_form = UnprocessedPaymentForm({'cart_id': 1, 'price': 10}) if (incoming_form.is_valid()): form_data = generate_wspay_form_data( incoming_form.cleaned_data.copy(), RequestFactory().get('/') ) req = WSPayRequest.objects.get() return_data['ShoppingCartID'] = str(req.request_uuid) return_data['Signature'] = generate_signature([ shop_id, secret_key, str(req.request_uuid), secret_key, '1000', secret_key, ]) assert return_data == form_data
def status_check(request_uuid): """Check status of a transaction.""" version = '2.0' shop_id = resolve(settings.WS_PAY_SHOP_ID) secret_key = resolve(settings.WS_PAY_SECRET_KEY) shopping_cart_id = str(request_uuid) signature = generate_signature([ shop_id, secret_key, shopping_cart_id, secret_key, shop_id, shopping_cart_id ]) data = { 'Version': version, 'ShopId': shop_id, 'ShoppingCartId': shopping_cart_id, 'Signature': signature } r = requests.post(f'{get_services_endpoint()}/statusCheck', data=data) return process_transaction_report( verify_transaction_report(WSPayTransactionReportForm, r.json()))
def verify_transaction_report(form_class, data): """Verify validity and authenticity of wspay transaction report.""" form = form_class(data=data) if form.is_valid(): signature = form.cleaned_data['Signature'] shop_id = resolve(settings.WS_PAY_SHOP_ID) secret_key = resolve(settings.WS_PAY_SECRET_KEY) param_list = [ shop_id, secret_key, form.cleaned_data['ActionSuccess'], form.cleaned_data['ApprovalCode'], secret_key, shop_id, form.cleaned_data['ApprovalCode'], form.cleaned_data['WsPayOrderId'], ] expected_signature = generate_signature(param_list) if signature != expected_signature: raise ValidationError('Bad signature') return form.cleaned_data raise ValidationError('Form is not valid')
def verify_response(form_class, data): """Verify validity and authenticity of wspay response.""" form = form_class(data=data) if form.is_valid(): signature = form.cleaned_data['Signature'] shop_id = resolve(settings.WS_PAY_SHOP_ID) secret_key = resolve(settings.WS_PAY_SECRET_KEY) param_list = [ shop_id, secret_key, data['ShoppingCartID'], secret_key, data['Success'], secret_key, data['ApprovalCode'], secret_key, ] expected_signature = generate_signature(param_list) if signature != expected_signature: raise ValidationError('Bad signature') return form.cleaned_data raise ValidationError('Form is not valid')
def dispatch(self, request, *args, **kwargs): form_class, request_status, redirect_setting = self._unpack_response_status( kwargs['status'] ) data = request.POST if request.method == 'POST' else request.GET wspay_request = process_response_data( verify_response(form_class, data), request_status ) kwargs = { 'cart_id': wspay_request.cart_id, 'request_uuid': wspay_request.request_uuid, } redirect_url = resolve( redirect_setting, **kwargs) process_response_pre_redirect.send_robust( self.__class__, wspay_request=wspay_request, http_request=request, redirect_url=redirect_url, ) return redirect(redirect_url)
def generate_wspay_form_data(input_data, request, additional_data=''): """Process incoming data and prepare for POST to WSPay.""" wspay_request = WSPayRequest.objects.create( cart_id=input_data['cart_id'], additional_data=additional_data, ) # Send a signal pay_request_created.send_robust(WSPayRequest, instance=wspay_request) input_data['cart_id'] = str(wspay_request.request_uuid) price = input_data['price'] assert price > 0, 'Price must be greater than 0' total_for_sign, total = build_price(price) shop_id = resolve(settings.WS_PAY_SHOP_ID) secret_key = resolve(settings.WS_PAY_SECRET_KEY) signature = generate_signature([ shop_id, secret_key, input_data['cart_id'], secret_key, total_for_sign, secret_key, ]) return_data = { 'ShopID': shop_id, 'ShoppingCartID': input_data['cart_id'], 'Version': resolve(settings.WS_PAY_VERSION), 'TotalAmount': total, 'Signature': signature, 'ReturnURL': request.build_absolute_uri( reverse('wspay:process-response', kwargs={'status': 'success'})), 'CancelURL': request.build_absolute_uri( reverse('wspay:process-response', kwargs={'status': 'cancel'})), 'ReturnErrorURL': request.build_absolute_uri( reverse('wspay:process-response', kwargs={'status': 'error'})), 'ReturnMethod': 'POST', } if input_data.get('first_name'): return_data['CustomerFirstName'] = input_data['first_name'] if input_data.get('last_name'): return_data['CustomerLastName'] = input_data['last_name'] if input_data.get('address'): return_data['CustomerAddress'] = input_data['address'] if input_data.get('city'): return_data['CustomerCity'] = input_data['city'] if input_data.get('zip_code'): return_data['CustomerZIP'] = input_data['zip_code'] if input_data.get('country'): return_data['CustomerCountry'] = input_data['country'] if input_data.get('email'): return_data['CustomerEmail'] = input_data['email'] if input_data.get('phone'): return_data['CustomerPhone'] = input_data['phone'] return return_data
def get_services_endpoint(): """Return production or dev services endpoint based on DEVELOPMENT setting.""" development = resolve(settings.WS_PAY_DEVELOPMENT) if development: return 'https://test.wspay.biz/api/services' return 'https://secure.wspay.biz/api/services'
def get_form_endpoint(): """Return production or dev endpoint based on DEVELOPMENT setting.""" development = resolve(settings.WS_PAY_DEVELOPMENT) if development: return 'https://formtest.wspay.biz/authorization.aspx' return 'https://form.wspay.biz/authorization.aspx'
def test_conf_resolver(): """Test conf resolver when settings are callables or dotted path to a callable.""" assert resolve(settings.WS_PAY_SHOP_ID) == 'MojShop' assert resolve(settings.WS_PAY_SECRET_KEY) == '3DfEO2B5Jjm4VC1Q3vEh'