(this is not a full solution on how to log in, but you did too many mistakes to simply add it as comment text, so i'll add my complaints as an answer
instead..)
1: this website use cookies for login sessions, you need to fetch a cookie (with a simple GET request) before logging in, you didn't.
2: you're trying to send the username and password encoded in multipart/form-data
-format (which is what you get if you give an array to CURLOPT_POSTFIELDS
), but this website use JSON to log in, so you need to send the username and password json-encoded.
3: you need to send an OPTIONS request to api.circle.com/api/v2/customers/0/sessions with a valid cookie before attempting to log in, but you didn't.
4: this website uses a variation of a CSRF token here called X-ECP-Session-Id
which you receive together with the GET request where you get the cookie, which you need to add to the login request, but you never fetched the token in the first place.
4: this website requires several custom http headers for the login request which you didn't add, specifically Accept: application/json, text/plain, */*
and Content-Type: application/json;charset=UTF-8
and X-App-Id
and X-App-Version
and X-Device-Id
and finally the CSRF token X-ECP-Session-Id
- you didn't add any of them.
no wonder your login request didn't succeed.
here's a login example using hhb_curl:
<?php
declare (strict_types = 1);
require_once('hhb_.inc.php');
const USERNAME = '???';
const PASSWORD = '???';
$hc = new hhb_curl('', true);
//now to fetch the csrf token and cookies
$html = $hc->exec('https://pay.circle.com/signin')->getStdOut();
{
// we need stuff from the headers
$cookies_raw = $hc->getResponseHeaders();
$cookies_parsed = array();
foreach ($cookies_raw as $tmp) {
if (false === strpos($tmp, ':')) {
// don't need this header
continue;
}
$tmp = explode(':', $tmp, 2);
$cookies_parsed[trim($tmp[0])] = trim($tmp[1]);
}
// 'X-App-Version: ff40150fdea82e0f0cca84b649cabc06cc2955d3'
// 'X-Device-Id: {"fingerprint":"aa1683cf1ce7dcca7a347b887747896b","fingerprintVersion":"1.1.0","fingerprintCookie":"6ad46a77-0c7e-4bd7-8570-1568f2ec8b9c"}',
// 'X-ECP-Session-Id: 0000188520190306085938487417',
//hhb_var_dump($cookies_parsed) & die();
assert(isset($cookies_parsed['X-App-Version']));
// TODO: X-Device-Id apparently comes from https://assets.circle.com/assets/javascripts/application-63066bd45540e4f6a74081aaaf96154f.js
// assert(isset($cookies_parsed['X-Device-Id']));
assert(isset($cookies_parsed['X-ECP-Session-Id']));
$login_headers = array(
'Content-Type: application/json;charset=utf-8',
'X-App-Id: angularjs',
'X-App-Version: ' . $cookies_parsed['X-App-Version'],
// TODO: 'X-Device-Id: ' . $cookies_parsed['X-Device-Id'],
'X-ECP-Session-Id: ' . $cookies_parsed['X-ECP-Session-Id'],
);
//hhb_var_dump($login_headers) & die();
}
$domd = @DOMDocument::loadHTML($html);
// now do the OPTIONS request. we need to do it to log in, but it doesn't return any useful data.
$hc->setopt_array(array(
CURLOPT_CUSTOMREQUEST => 'OPTIONS',
CURLOPT_URL => 'https://api.circle.com/api/v2/customers/0/sessions',
CURLOPT_HTTPHEADER => array(
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Access-Control-Request-Method: POST',
'Access-Control-Request-Headers: content-type,x-app-id,x-app-version,x-device-id,x-ecp-session-id',
'Origin: https://pay.circle.com',
)
))->exec()->setopt(CURLOPT_CUSTOMREQUEST, null);
// now to do the actual login
$json = $hc->setopt_array(array(
CURLOPT_URL => 'https://api.circle.com/api/v2/customers/0/sessions',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => json_encode(array('email' => USERNAME, 'password' => PASSWORD)),
CURLOPT_HTTPHEADER => $login_headers
))->exec()->getStdOut();
$parsed = json_decode($json, true);
// now to look for login errors
$login_errors = [];
/*
{
"response": {
"status": {
"code": 4,
"customerState": {
"isEmailVerified": false,
"isMfaVerified": false
}
},
"errors": {
"emailOrPassword": ["invalid"]
},
"displayErrors": ["Email or password is incorrect"],
"displayErrorsMap": {
"emailOrPassword": "Email or password is incorrect"
}
}
}
*/
$response=$parsed['response'];
if(!empty($response['errors'])){
$login_errors[]=$response['errors'];
}
if(!empty($response['displayErrors'])){
$login_errors[]=$response['displayErrors'];
}
if(!empty($response['displayErrors'])){
$login_errors[]=$response['displayErrorsMap'];
}
if(!empty($login_errors)){
echo "LOGIN ERRORS!
";
var_dump($login_errors);
throw new \RuntimeException("LOGIN ERRORS!");
}
var_dump($json);
which outputs:
$ php wtf2.php
LOGIN ERRORS!
array(3) {
[0]=>
array(1) {
["emailOrPassword"]=>
array(1) {
[0]=>
string(7) "invalid"
}
}
[1]=>
array(1) {
[0]=>
string(30) "Email or password is incorrect"
}
[2]=>
array(1) {
["emailOrPassword"]=>
string(30) "Email or password is incorrect"
}
}
PHP Fatal error: Uncaught RuntimeException: LOGIN ERRORS! in /cygdrive/c/projects/misc/wtf2.php:94
Stack trace:
#0 {main}
thrown in /cygdrive/c/projects/misc/wtf2.php on line 94
because ???
is not a valid username, see line 4 & 5.