Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSON::Transaction::MerchantAuthorize #53

Merged
merged 2 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,51 @@ You can check the trasaction [status](https://api-reference.datatrans.ch/#tag/v1
end
```

Merchant Initiated Payments
---------

It's possible to authorize transactions without user interaction, via [merchant initiated payments](https://docs.datatrans.ch/docs/merchant-initiated-payments).

To perform a so-called "dedicated registration" (so we can later charge the card via its `alias`), you should follow the same steps as described above, but not provide an amount:
visini marked this conversation as resolved.
Show resolved Hide resolved

```ruby
transaction = datatrans.json_transaction(
refno: 'ABCDEF',
amount: 0, # omit amount for dedicated registrations
currency: "CHF",
payment_methods: ["ECA", "VIS"],
success_url: <your_application_return_url>,
cancel_url: <your_application_return_url>,
error_url: <your_application_return_url>
)

init = transaction.authorize

# successful authorization call returns in response a transaction id
if init
transaction_id = transaction.response.params["transactionId"]
end
```

Then, at a later point in time, and without needing any user interaction, you can create a payment via `merchant_authorize`:

```ruby
dedicated_registration = datatrans.json_transaction(transaction_id: transaction_id)
dedicated_registration.status # this will contain the card information

card_alias = dedicated_registration.response.params["card"]["alias"]
card_expiry_month = dedicated_registration.response.params["card"]["expiryMonth"]
card_expiry_year = dedicated_registration.response.params["card"]["expiryYear"]

transaction = datatrans.json_transaction(
refno: "ABCDEF",
amount: 1000,
currency: "CHF",
card: {alias: card_alias, expiryMonth: card_expiry_month, expiryYear: card_expiry_year}
)

transaction.merchant_authorize # this will charge the card without user interaction
```

XML Transactions
================
Expand Down
3 changes: 3 additions & 0 deletions lib/datatrans/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def url(what, options = {})
when :init_transaction
subdomain = SUBDOMAINS[:server_to_server_api]
path = "/v1/transactions"
when :authorize_transaction
subdomain = SUBDOMAINS[:server_to_server_api]
path = "/v1/transactions/authorize"
when :start_json_transaction
subdomain = SUBDOMAINS[:payment_page]
path = "/v1/start/#{options[:transaction_id]}"
Expand Down
7 changes: 7 additions & 0 deletions lib/datatrans/json/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ def authorize
@response.successful?
end

def merchant_authorize
self.request = MerchantAuthorize.new(self.datatrans, params)
@response = MerchantAuthorizeResponse.new(self.datatrans, request.process)
@response.successful?
end

def status
self.request = Status.new(self.datatrans, params)
@response = StatusResponse.new(self.datatrans, request.process)
Expand All @@ -29,4 +35,5 @@ def transaction_path
end

require 'datatrans/json/transaction/authorize'
require 'datatrans/json/transaction/merchant_authorize'
require 'datatrans/json/transaction/status'
45 changes: 45 additions & 0 deletions lib/datatrans/json/transaction/merchant_authorize.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'httparty'
require 'datatrans/json/transaction/response'

class Datatrans::JSON::Transaction
class MerchantAuthorize
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datatrans::JSON::Transaction::Authorize is actually not for the authorize operation but rather for the init operation.

yeah this rather confusing ... I propose to rename Datatrans::JSON::Transaction::Authorize to Datatrans::JSON::Transaction::Initialize, but create an alias for it (for now).

Once we release the breaking change where the XML API is removed, we can alias this class to Authorize.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I know what you mean but I'm not sure in what order it would be best to rename / alias / deprecate... If you propose something, I can review it 🙏

# class to authorize a transaction without user interaction https://api-reference.datatrans.ch/#tag/v1transactions/operation/authorize
attr_accessor :params, :datatrans

def initialize(datatrans, params)
@datatrans = datatrans
@params = params
end

def post(url, options = {})
options = options
.merge(self.datatrans.proxy)
.merge(:basic_auth => { :username => self.datatrans.merchant_id, :password => self.datatrans.password })
HTTParty.post(url, **options)
end

def process
post(self.datatrans.url(:authorize_transaction),
:headers => { 'Content-Type' => 'application/json' },
:body => request_body.to_json).parsed_response
end

def request_body
auto_settle = params[:auto_settle].nil? ? true : params[:auto_settle]

{
"currency": params[:currency],
"refno": params[:refno],
"amount": params[:amount],
"card": params[:card],
"autoSettle": auto_settle,
}
end
end

class MerchantAuthorizeResponse < Response
def successful?
params["error"].blank? && params["transactionId"].present?
end
end
end
97 changes: 97 additions & 0 deletions spec/json/merchant_authorize_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require "spec_helper"

describe Datatrans::JSON::Transaction::MerchantAuthorize do
before do
@successful_response = {
"transactionId" => "230223022302230223",
"acquirerAuthorizationCode" => "123456",
"card" => {
"masked" => "411111xxxxxx1111"
}
}

@failed_response = {
"error" => {
"code" => "INVALID_PROPERTY",
"message" => "authorize.card.alias or number is mandatory"
}
}

@valid_params = {
currency: "CHF",
refno: "B4B4B4B4B",
amount: 1000,
card: {
alias: "AAABcH0Bq92s3kgAESIAAbGj5NIsAHWC",
expiryMonth: "01",
expiryYear: "23"
},
auto_settle: true
}

@expected_request_body = {
"currency": "CHF",
"refno": "B4B4B4B4B",
"amount": 1000,
"card": {
"alias": "AAABcH0Bq92s3kgAESIAAbGj5NIsAHWC",
"expiryMonth": "01",
"expiryYear": "23"
},
"autoSettle": true
}

@invalid_params = {
currency: "CHF",
refno: "B4B4B4B4B",
amount: 1000,
card: {
expiryMonth: "01",
expiryYear: "23"
}
}
end

context "successful response" do
before do
allow_any_instance_of(Datatrans::JSON::Transaction::MerchantAuthorize).to receive(:process).and_return(@successful_response)
end

it "generates correct request_body" do
request = Datatrans::JSON::Transaction::MerchantAuthorize.new(@datatrans, @valid_params)
expect(request.request_body).to eq(@expected_request_body)
end

it "#process handles a valid datatrans merchant authorize response" do
transaction = Datatrans::JSON::Transaction.new(@datatrans, @valid_params)
expect(transaction.merchant_authorize).to be true
end
end

context "with autoSettle specified" do
it "handles autoSettle correctly in request_body" do
params_with_auto_settle = @valid_params.merge(auto_settle: false)
request = Datatrans::JSON::Transaction::MerchantAuthorize.new(@datatrans, params_with_auto_settle)

expected_request_body_without_auto_settle = @expected_request_body.merge(autoSettle: false)
expect(request.request_body).to eq(expected_request_body_without_auto_settle)
end
end

context "failed response" do
before do
allow_any_instance_of(Datatrans::JSON::Transaction::MerchantAuthorize).to receive(:process).and_return(@failed_response)
@transaction = Datatrans::JSON::Transaction.new(@datatrans, @invalid_params)
end

it "#process handles a failed datatrans merchant authorize response" do
expect(@transaction.merchant_authorize).to be false
end

it "returns error details" do
@transaction.merchant_authorize
expect(@transaction.response.error_code).to eq "INVALID_PROPERTY"
expect(@transaction.response.error_message).to eq "authorize.card.alias or number is mandatory"
end
end
end