This function for populating the attributes of a class, using a DB model and REST API
GeoIPUtils being a utility of methods, with the CamsModel being more of an object? camsModel could then accept an instance of GeoIPUtils rather than assigning in the separate procedural logic below:

$geoUtils = new GeoIPUtils();
$loginIps = $this-&gt;camsModel-&gt;getLoginIpsFromLoginHistory($this-&gt;loginHistory);
$this-&gt;loginIpInformation = $geoUtils-&gt;getIPAddressInfo($loginIps);


CamsModel always benefit from a GeoIPUtils instance? If so could be in the constructor. If wish to keep CamsModel, GeoIPUtils responsibilities better separated, add more indirection with a separate object which uses instances of CamsModel and GeoIPUtils as needed. Refactor generateReport into a Report object which requires a CamsModel instance, a GeoIPUtils instance, and your current this state. Creating a new Report object would replace:

$this-&gt;camsModel-&gt;getLastIntTransactionsByUser($this-&gt;userId, 20);


with:

report.getTransactions();


or:

report.getTransactions(20);


If the CamsModel class changed, then your current this or class in the question code above wouldn't need to be modified. Suppose the new version of CamsModel did not have a getIDUrl method. Only Report would be concerned with the details of CamsModel. Your this would depend only upon whatever methods Report provides. GeoIPUtils and CamsModel are now divorced from your current this class. 

Replace 20, 100000 and any other literals in code with meaningful constants.

report.getTransactions(USERID_LIMIT);


Writing unit tests would be done first before crafting the object or method code, except for empty placeholder stub classes and methods. Get known input, desired output or object state for the non-existent class or method. Write tests which require that output or object state.

unit tests begin with comparison for equality. Simple String or other literals of the desired output or values are compared with your new object's envisioned getter methods, if( report.getChargebackTotal() != 959.30) failTest("Charge back total invalid");. Once unit test is written, then at this point begin writing the actual code to produce that output. 

Will rewrite this code repeatedly, adding new objects and methods, painfully deleting code just typed, but writing the unit tests first is well worth the effort. Add unit tests for unexpected input, boundary testing, very small, large, negative values. Use regular expressions to whitelist and reject all input before validating by blacklisting. Protect against buffer overflow, exceeding array boundaries, numeric and String types. Modify code to validate all input, all return values. Fail immediately whenever out of range or unexpected.