The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
SYNOPSIS

    If you just want to download banking statements, and you use
    Linux/Unix, you might want to use the download-mandiri script instead
    of having to deal with this library directly.

    If you want to use the library in your Perl application:

        use Finance::Bank::ID::Mandiri;
    
        # FBI::Mandiri uses Log::Any. to show logs, use something like:
        use Log::Log4perl qw(:easy);
        use Log::Any::Adapter;
        Log::Log4perl->easy_init($DEBUG);
        Log::Any::Adapter->set('Log4perl');
    
        my $ibank = Finance::Bank::ID::Mandiri->new(
            username => '....', # optional if you're only using parse_statement()
            password => '....', # idem
            verify_https => 1,          # default is 0
            #https_ca_dir => '/etc/ssl/certs', # default is already /etc/ssl/certs
        );
    
        eval {
            $ibank->login(); # dies on error
    
            my $accts = $ibank->list_accounts();
    
            my $bal = $ibank->check_balance($acct); # $acct is optional
    
            my $stmt = $ibank->get_statement(
                account    => ..., # opt, default account used if not undef
                days       => 30,  # opt
                start_date => DateTime->new(year=>2009, month=>10, day=>6),
                                   # opt, takes precedence over 'days'
                end_date   => DateTime->today, # opt, takes precedence over 'days'
            );
    
            print "Transactions: ";
            for my $tx (@{ $stmt->{transactions} }) {
                print "$tx->{date} $tx->{amount} $tx->{description}\n";
            }
        };
    
        # remember to call this, otherwise you will have trouble logging in again
        # for some time
        if ($ibank->logged_in) { $ibank->logout() }
    
        # utility routines
        my $res = $ibank->parse_statement($html);

DESCRIPTION

    This module provide a rudimentary interface to the web-based online
    banking interface of the Indonesian Bank Mandiri at
    https://ib.bankmandiri.co.id (henceforth IB). You will need either
    Crypt::SSLeay or IO::Socket::SSL installed for HTTPS support to work
    (and strictly Crypt::SSLeay to enable certificate verification).
    WWW::Mechanize is required but you can supply your own mech-like
    object.

    Aside from the above site for invididual accounts, there are also 2
    other sites for corporate accounts:
    https://cms.bankmandiri.co.id/ecbanking/ (henceforth CMS) and
    https://mcm.bankmandiri.co.id/ (henceforth MCM). CMS is the older
    version and as of the end of Sept, 2010 has been discontinued.

    This module currently can only login to IB and not CMS/MCM, but this
    module can parse statement page from all 3 sites. For CMS version, only
    text version [copy paste result] is currently supported and not HTML.
    For MCM, only semicolon format is currently supported.

    Warning: This module is neither offical nor is it tested to be 100%
    safe! Because of the nature of web-robots, everything may break from
    one day to the other when the underlying web interface changes.

WARNING

    This warning is from Simon Cozens' Finance::Bank::LloydsTSB, and seems
    just as apt here.

    This is code for online banking, and that means your money, and that
    means BE CAREFUL. You are encouraged, nay, expected, to audit the
    source of this module yourself to reassure yourself that I am not doing
    anything untoward with your banking data. This software is useful to
    me, but is provided under NO GUARANTEE, explicit or implied.

ERROR HANDLING AND DEBUGGING

    Most methods die() when encountering errors, so you can use eval() to
    trap them.

    Full response headers and bodies are dumped to a separate logger. See
    documentation on new() below and the sample script in examples/
    subdirectory in the distribution.

ATTRIBUTES

METHODS

 new(%args)

    Create a new instance. %args keys:

      * username

      Optional if you are just using utility methods like parse_statement()
      and not login() etc.

      * password

      Optional if you are just using utility methods like parse_statement()
      and not login() etc.

      * mech

      Optional. A WWW::Mechanize-like object. By default this module
      instantiate a new Finance::BankUtils::ID::Mechanize (a WWW::Mechanize
      subclass) object to retrieve web pages, but if you want to use a
      custom/different one, you are allowed to do so here. Use cases
      include: you want to retry and increase timeout due to
      slow/unreliable network connection (using
      WWW::Mechanize::Plugin::Retry), you want to slow things down using
      WWW::Mechanize::Sleepy, you want to use IE engine using
      Win32::IE::Mechanize, etc.

      * verify_https

      Optional. If you are using the default mech object (see previous
      option), you can set this option to 1 to enable SSL certificate
      verification (recommended for security). Default is 0.

      SSL verification will require a CA bundle directory, default is
      /etc/ssl/certs. Adjust https_ca_dir option if your CA bundle is not
      located in that directory.

      * https_ca_dir

      Optional. Default is /etc/ssl/certs. Used to set HTTPS_CA_DIR
      environment variable for enabling certificate checking in
      Crypt::SSLeay. Only used if verify_https is on.

      * logger

      Optional. You can supply a Log::Any-like object here. If not
      specified, this module will use a default logger.

      * logger_dump

      Optional. You can supply a Log::Any-like object here. This is just
      like logger but this module will log contents of response bodies here
      for debugging purposes. You can use with something like
      Log::Dispatch::Dir to save web pages more conveniently as separate
      files.

 login()

    Login to the net banking site. You actually do not have to do this
    explicitly as login() is called by other methods like check_balance()
    or get_statement().

    If login is successful, logged_in will be set to true and subsequent
    calls to login() will become a no-op until logout() is called.

    Dies on failure.

 logout()

    Logout from the net banking site. You need to call this at the end of
    your program, otherwise the site will prevent you from re-logging in
    for some time (e.g. 10 minutes).

    If logout is successful, logged_in will be set to false and subsequent
    calls to logout() will become a no-op until login() is called.

    Dies on failure.

 list_accounts()

 check_balance([$acct])

 get_statement(%args) => $stmt

    Get account statement. %args keys:

      * account

      Optional. Select the account to get statement of. If not specified,
      will use the already selected account.

      * days

      Optional. Number of days. If days is 1, then start date and end date
      will be the same.

      * start_date

      Optional. Default is end_date - 1 month, which seems to be the
      current limit set by the bank (for example, if end_date is
      2013-03-08, then start_date will be set to 2013-02-08). If not set
      and days is set, will be set to end_date - days.

      * end_date

      Optional. Default is today (or some 1+ days from today if today is a
      Saturday/Sunday/holiday, depending on the default value set by the
      site's form).

 parse_statement($html, %opts) => $res

    Given the HTML of the account statement results page, parse it into
    structured data:

     $stmt = {
        start_date     => $start_dt, # a DateTime object
        end_date       => $end_dt,   # a DateTime object
        account_holder => STRING,
        account        => STRING,    # account number
        currency       => STRING,    # 3-digit currency code
        transactions   => [
            # first transaction
            {
              date        => $dt, # a DateTime object, book date ("tanggal pembukuan")
              seq         => INT, # a number >= 1 which marks the sequence of transactions for the day
              amount      => REAL, # a real number, positive means credit (deposit), negative means debit (withdrawal)
              description => STRING,
              branch      => STRING, # 4-digit branch/ATM code, only for MCM
            },
            # second transaction
            ...
        ]
     }

    Returns:

     [$status, $err_details, $stmt]

    $status is 200 if successful or some other 3-letter code if parsing
    failed. $stmt is the result (structure as above, or undef if parsing
    failed).

    Options:

      * return_datetime_obj => BOOL

      Default is true. If set to false, the method will return dates as
      strings with this format: 'YYYY-MM-DD HH::mm::SS' (produced by
      DateTime->dmy . ' ' . DateTime->hms). This is to make it easy to pass
      the data structure into YAML, JSON, MySQL, etc. Nevertheless,
      internally DateTime objects are still used.

    Additional notes:

    The method can also (or used to) handle copy-pasted text from the GUI
    browser, but this is no longer documented or guaranteed to keep
    working.

FAQ

 (2014) I'm getting error message: "Can't connect to
 ib.bankmandiri.co.id:443 at ..."

    Try upgrading your IO::Socket::SSL. It stalls with IO::Socket::SSL
    version 1.76, but works with newer versions (e.g. 1.989).