Question

I'm out of my depth with curl. I want to integrate PayMill into my site (which is written in Perl). There isn't a Perl lib for Paymill yet, so I need to connect to them via curl.

I have completed the front end JS Paymill integration, and received a payment token from PayMill.

I now need to pass the token received from Paymill to my backend and use curl to ask PayMill to complete the transaction and charge the user. At this point I'm stuck.

To make a transaction, the PayMill documentation says that I must do the following:

curl https://api.paymill.de/v2/transactions \
-u b94a7550bd908877cbae5d3cf0dc4b74: \
-d "amount=4200" \
-d "currency=EUR" \
-d "token=098f6bcd4621d373cade4e832627b4f6" \
-d "description=Test Transaction"

I believe -u is the Paymill secret key to authenticate my request although the documentation is not clear here.

I've had a look at WWW::Curl::Easy, Net:Curl::Easy and LWP::Curl, however nothing in the documentation for those methods makes it obvious to me how to form the query above.

I've tried (without really believing it would work), simply encoding a string in perl as described above;

my $request = '-u ' . $private_key . " ";
foreach my $key (keys %$params_in) {
    $request .= '-d "' . lc($key) .'='.$params_in->{$key} . ' ';
}

And then passing $request to my attempt at curl as follows;

my $curl = WWW::Curl::Easy->new;
$curl->setopt(WWW::Curl::Easy::CURLOPT_HEADER(), 1);
$curl->setopt(WWW::Curl::Easy::CURLOPT_URL(), $paymill_server);
$curl->setopt(WWW::Curl::Easy::CURLOPT_POST(), 1);
$curl->setopt(WWW::Curl::Easy::CURLOPT_POSTFIELDS(), $request);

my $response;
$curl->setopt(WWW::Curl::Easy::CURLOPT_WRITEDATA(), \$response);

my $retcode = $curl->perform;

however that fails with an Access Denied error, which I assume is because Paymill is not finding my key because I'm messing up the Curl (assuming -u is supposed to be the secret_key).

I feel I'm missing something obvious here.

Could someone point me in the right direction re how to do this? Thanks

UPDATE

Excellent answers, thanks everyone for your help, it's working now. I went with Matthias's solution in the end and the final complete solution for making a transaction looked as follows;

use LWP::UserAgent;
use MIME::Base64;
use JSON::XS;

my $ua = LWP::UserAgent->new;
$ua->default_header(Authorization => "Basic " . encode_base64(private_key));

my $response = $ua->post(https://api.paymill.de:443/v2/transactions , $params );
if ( $response->is_success ) {
    my $obj = eval { decode_json $response->content } || {};
    etc
}
Was it helpful?

Solution

Like other answers propose the best way would be using LWP::UserAgent for doing the requests.

Edit: Since PAYMILL is sending challenge responses since a while now I updated the code.

Since Paymill doesn't comply with RFC 2616, Section 14.47 (the API isn't sending a challenge response) LWP::UserAgent and similar are failing in sending a second request with the credentials. The solution is to "force" LWP::UserAgent to send the credentials with the first request by adding them as header:

use LWP::UserAgent;
use MIME::Base64;

my $ua = LWP::UserAgent->new;
# Use the following line no longer:
# $ua->default_header(Authorization => "Basic " . encode_base64("your PRIVATE key"))
$ua->credentials('api.paymill.de:443', '', 'YOUR PRIVATE KEY');

# Dumping only
use Data::Dumper;
print Dumper($ua->get("https://api.paymill.de:443/v2/clients"));

Disclosure: I work at Paymill.

OTHER TIPS

I don't know if the authentication part with the user/password and your token is correct as I don't know what the 'Realm' is supposed to be. Still, have a go with LWP. It's not that I don't like Curl, I just don't know it, but I do know LWP.

use strict; use warnings;
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->credentials(
  'api.paymill.de:80',
  'Realm?',
  'b94a7550bd908877cbae5d3cf0dc4b74'
);
my $response = $ua->post(
  ' https://api.paymill.de/v2/transactions',
  {
    amount      => "4200",
    currency    => "EUR",
    token       => "098f6bcd4621d373cade4e832627b4f6",
    description => "Test Transaction",
  }
);
if ( $response->is_success ) {
  print $response->decoded_content;    # or whatever
} else {
  die $response->status_line;
}

Edit: I read a little in the Paymill documentation. It says:

Authentication

Example

% curl https://api.paymill.de/v2/clients \ -u e73fa5e7b87620585b5ea5d73c4d23bb:

To authenticate at the Paymill API, you need the private key of your test or live account. You have to use http basic access authentification. Your key has to be set as the username. A password isn’t required and you don’t have to insert one. But if you want, feel free to insert an arbitrary string.

Note

Please keep your private keys secure and don’t pass them to anybody. These private keys have extreme secure information for

handling the transactions of your shop. All your requests must be made via https. Requests which will be made in another way will fail. This is for security reasons of the submitted data.

There is also a link to http://en.wikipedia.org/wiki/HTTP_Secure, which clears up the -u part pretty much I believe.

You can use LWP::Protocol::Net::Curl to integrate LWP and libcurl organically. Check this:

#!/usr/bin/env perl
use common::sense;

use Data::Printer;
use JSON::XS;
use LWP::Protocol::Net::Curl verbose => 1;
use LWP::UserAgent;

# create user agent
my $ua = LWP::UserAgent->new;

# POST request
my $res = $ua->post(
    'https://b94a7550bd908877cbae5d3cf0dc4b74:@api.paymill.de/v2/transactions',
    'Accept-Encoding' => 'gzip',
    Content => {
        amount      => 4200,
        currency    => 'EUR',
        token       => '098f6bcd4621d373cade4e832627b4f6',
        description => 'Test Transaction',
    },
);

# parse received data
my $obj = eval { decode_json $res->content } // {};

# output
p $obj;

The output:

* About to connect() to api.paymill.de port 443 (#0)
*   Trying 62.138.241.3...
* Connected to api.paymill.de (62.138.241.3) port 443 (#0)
* Connected to api.paymill.de (62.138.241.3) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
* SSL connection using RC4-SHA
* Server certificate:
*    subject: OU=Domain Control Validated; OU=PositiveSSL Wildcard; CN=*.paymill.de
*    start date: 2012-07
*    expire date: 2013-10
*    subjectAltName: api.paymill.de matched
*    issuer: C=GB; S
*    SSL certificate verify ok.
* Server auth using Basic with user 'b94a7550bd908877cbae5d3cf0dc4b74'
> POST /v2/transactions HTTP/1.1
Authorization: Basic Yjk0YTc1NTBiZDkwODg3N2NiYWU1ZDNjZjBkYzRiNzQ6
User-Agent: libwww-perl/6.04 libcurl/7.28.0 OpenSSL/1.0.0e zlib/1.2.3.4 libidn/1.22 libssh2/1.2.8
Host: api.paymill.de
Accept: */*
Accept-Encoding: gzip
Content-Length: 92
Content-Type: application/x-www-form-urlencoded

* upload completely sent off: 92 out of 92 bytes
< HTTP/1.1 200 OK
< Server: nginx
< Date: Wed, 09 Jan 2013 17:22:54 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: close
< Set-Cookie: PHPSESSID=rmdo5a8c6u107gma28lotmmn24; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< X-Server: hrtt-frn5-de13
< 
* Closing connection #0
Printing in line 28 of paymill.pl:
\ {
    data   {
        amount             4200,
        client             {
            created_at     1357752174,
            description    undef,
            email          undef,
            id             "client_85cb0bfc837f31c81015",
            payment        [],
            subscription   undef,
            updated_at     1357752174
        },
        created_at         1357752174,
        currency           "EUR",
        description        "Test Transaction",
        id                 "tran_c672daa0538e2a04e919",
        livemode           false,
        origin_amount      4200,
        payment            {
            card_holder    undef,
            card_type      "visa",
            client         "client_85cb0bfc837f31c81015",
            country        undef,
            created_at     1357752174,
            expire_month   12,
            expire_year    2014,
            id             "pay_2732689f44928301c769",
            last4          1111,
            type           "creditcard",
            updated_at     1357752174
        },
        preauthorization   undef,
        refunds            undef,
        status             "closed",
        updated_at         1357752174
    },
    mode   "test"
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top