Full Code of biscolab/laravel-recaptcha for AI

master 440fc617cba9 cached
34 files
101.0 KB
25.7k tokens
134 symbols
1 requests
Download .txt
Repository: biscolab/laravel-recaptcha
Branch: master
Commit: 440fc617cba9
Files: 34
Total size: 101.0 KB

Directory structure:
gitextract_5ohrg6fm/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── pull_request_template.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── config/
│   └── recaptcha.php
├── phpunit.xml.dist
├── src/
│   ├── Controllers/
│   │   └── ReCaptchaController.php
│   ├── Exceptions/
│   │   └── InvalidConfigurationException.php
│   ├── Facades/
│   │   └── ReCaptcha.php
│   ├── ReCaptchaBuilder.php
│   ├── ReCaptchaBuilderInvisible.php
│   ├── ReCaptchaBuilderV2.php
│   ├── ReCaptchaBuilderV3.php
│   ├── ReCaptchaServiceProvider.php
│   └── helpers.php
└── tests/
    ├── ReCaptchaConfigurationTest.php
    ├── ReCaptchaCustomApiDomainTest.php
    ├── ReCaptchaHelpersInvisibleTest.php
    ├── ReCaptchaHelpersV2ExplicitTest.php
    ├── ReCaptchaHelpersV2Test.php
    ├── ReCaptchaInvalidConfigurationTest.php
    ├── ReCaptchaLangTest.php
    ├── ReCaptchaTest.php
    ├── ReCaptchaV3Test.php
    └── TestCase.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=false
indent_style=space
indent_size=4

[{*.yml,*.yaml}]
indent_style=space
indent_size=2



================================================
FILE: .gitattributes
================================================
/tests export-ignore
/.github export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.scrutinizer.yml export-ignore
/.travis.yml export-ignore
/.editorconfig export-ignore
/phpunit.xml export-ignore
/changelog.md export-ignore
/README.md export-ignore
/CONTRIBUTING.md export-ignore
/CODE_OF_CONDUCT.md export-ignore

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Environment:**
 - OS and server: [e.g. Ubuntu 16.04 / Nginx] (If you are using a VM on Windows DON'T "say" Windows)
 - PHP version [7.1.x]
 - Laravel version [e.g. 5.5]
 - Package version [e.g. 3.4.1]

**Additional context**
Add any other context about the problem here, like code snippets.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/pull_request_template.md
================================================
Bug Fixes **ONLY**. NO NEW FEATURE ACCEPTED!

<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->

## Related Issue
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots (if appropriate):


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  - push
  - pull_request

jobs:
  run:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-latest]
        php: [7.3, 7.4, '8.0', 8.1, '8.2']
        laravel: [7, 8, 9, 10, '11']
        exclude:
          - php: 7.3
            laravel: 10
          - php: 7.4
            laravel: 10
          - php: 8.0
            laravel: 10
          - php: 7.3
            laravel: 9
          - php: 7.4
            laravel: 9
          - laravel: '11'
            php: 7.3
          - laravel: '11'
            php: 7.4
          - laravel: '11'
            php: '8.0'
          - laravel: '11'
            php: 8.1

    name: PHP ${{ matrix.php }} Test on ${{ matrix.os }} – Laravel ${{ matrix.laravel }}

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{matrix.php}}

      - name: Install dependencies
        uses: php-actions/composer@v5
        with:
          args: --prefer-source --no-interaction

      - name: Run composer test script
        uses: php-actions/composer@v5
        with:
          command: test


================================================
FILE: .gitignore
================================================
vendor/
/.idea

# Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer
.rocketeer/

# composer
/composer.lock
/composer.local.*
/composer.phar

# coverage reports
/coverage.clover
coverage.xml
clover.xml
report/

# testing
.phpunit.result.cache
phpunit.xml.dist.bak

================================================
FILE: .scrutinizer.yml
================================================
build:
  nodes:
    coverage:
      tests:
        override:
          - command: vendor/bin/phpunit --coverage-clover=clover.xml
            coverage:
              file: clover.xml
              format: clover
tools:
    php_code_sniffer:
        config:
            standard: PSR2

================================================
FILE: .travis.yml
================================================
language: php

php:
  - 7.3
  - 7.4
  - 8.0
# PHP 8.1 isn't currently support by Travis CI
#  - 8.1

env:
  - ILLUMINATE_VERSION=^7.0 TESTBENCH_VERSION=5.*
  - ILLUMINATE_VERSION=^8.0 TESTBENCH_VERSION=6.*
  - ILLUMINATE_VERSION=^9.0 TESTBENCH_VERSION=^7.0

matrix:
  exclude:
    # Don't test Laravel 9 on PHP 7.3 or PHP 7.4, as the mininmum
    # required PHP version for this Laravel version is 8.0.2
    - php: 7.3
      env: ILLUMINATE_VERSION=^9.0 TESTBENCH_VERSION=^7.0
    - php: 7.4
      env: ILLUMINATE_VERSION=^9.0 TESTBENCH_VERSION=^7.0

before_install:
  - composer require "illuminate/routing:${ILLUMINATE_VERSION}" --no-update --prefer-dist
  - composer require "illuminate/support:${ILLUMINATE_VERSION}" --no-update --prefer-dist
  - composer require "orchestra/testbench:${TESTBENCH_VERSION}" --no-update --prefer-dist

install: travis_retry composer install --prefer-source --no-interaction

script:
  - composer test

fast_finish: true


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at roby.belotti@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

Contributions are **welcome** and will be fully **credited**.

We accept contributions via Pull Requests on [Github](https://github.com/biscolab/laravel-recaptcha).


## Pull Requests

- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).

- **Add tests!** - Your patch won't be accepted if it doesn't have tests.

- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.

- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.

- **Create feature branches** - Don't ask us to pull from your master branch.

- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.

- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.


## Running Tests

``` bash
$ composer test
```


**Happy coding**!


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 - present Roberto Belotti

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
**Laravel ReCAPTCHA** is a very simply-to-use Laravel 5 package to embed Google reCAPTCHA in your application.

[![Build Status](https://travis-ci.org/biscolab/laravel-recaptcha.svg?branch=master#img-thumbnail)](https://travis-ci.org/biscolab/laravel-recaptcha)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/biscolab/laravel-recaptcha/badges/quality-score.png?b=master#img-thumbnail)](https://scrutinizer-ci.com/g/biscolab/laravel-recaptcha/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/biscolab/laravel-recaptcha/badges/coverage.png?b=master#img-thumbnail)](https://scrutinizer-ci.com/g/biscolab/laravel-recaptcha/?branch=master)
[![Packagist version](https://img.shields.io/packagist/v/biscolab/laravel-recaptcha.svg#img-thumbnail)](https://packagist.org/packages/biscolab/laravel-recaptcha)
[![Downloads](https://img.shields.io/packagist/dt/biscolab/laravel-recaptcha.svg#img-thumbnail)](https://packagist.org/packages/biscolab/laravel-recaptcha/stats)
[![MIT License](https://img.shields.io/github/license/biscolab/laravel-recaptcha.svg#img-thumbnail)](https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE)

## What is reCAPTCHA?

Google developers says: "reCAPTCHA protects you against spam and other types of automated abuse. Here, we explain how to add reCAPTCHA to your site or application."

You can find further info at <a href="https://developers.google.com/recaptcha/intro" target="_blank" title="Google reCAPTCHA Developer's Guide">Google reCAPTCHA Developer's Guide</a>

## reCAPTCHA available versions

At this moment there are 3 versions available (for web applications):

- **v3**, the latest (<a href="https://developers.google.com/recaptcha/docs/v3" target="_blank">reCAPTCHA v3</a>)
- **v2 checkbox** or simply reCAPTCHA v2 (<a href="https://developers.google.com/recaptcha/docs/display" target="_blank">reCAPTCHA v2</a>)
- **v2 invisible** (<a href="https://developers.google.com/recaptcha/docs/invisible" target="_blank">Invisible reCAPTCHA</a>)

## Get your key first!

First of all you have to create your own API keys <a href="https://www.google.com/recaptcha/admin" target="_blank">here</a>

Follow the instructions and at the end of the process you will find **Site key** and **Secret key**. Keep them close..you will need soon!

## System requirements

| Package version | reCaptcha version             | PHP version           | Laravel version         | 
| --------------- | ----------------------------- | --------------------- | ----------------------- | 
| 6.1             | v3, v2 Invisible, v2 Checkbox | 7.3 or greater        | 7, 8, 9, 10, 11         | 
| 6.0             | v3, v2 Invisible, v2 Checkbox | 7.3 or greater        | 7, 8, 9, 10             | 
| 5.x             | v3, v2 Invisible, v2 Checkbox | 7.3 or greater        | 7, 8, 9                 | 
| 4.2.x to 4.4.x  | v3, v2 Invisible, v2 Checkbox | 7.1 or greater        | 5.5 or greater, 6, 7, 8 | 
| 4.1.x           | v3, v2 Invisible, v2 Checkbox | 7.1 or greater        | 5.5 or greater, 6, 7    | 
| 4.0.x           | v3, v2 Invisible, v2 Checkbox | 7.1 or greater        | 5.5 or greater, 6       | 
| 3.x             | v3, v2 Invisible, v2 Checkbox | 7.1 or greater        | 5.5 or greater, 6 (\*)  | 
| 2.x             | v2 Invisible, v2 Checkbox     | 5.5.9, 7.0 or greater | 5.0 or greater          | 

> (\*) Version 3.6.1 is Laravel 6 ready

## Composer

You can install the package via composer:

```sh
$ composer require biscolab/laravel-recaptcha
```

Laravel 5.5 (or greater) uses package auto-discovery, so doesn't require you to manually add the Service Provider, but if you don't use auto-discovery `ReCaptchaServiceProvider` must be registered in `config/app.php`:

```php
'providers' => [
    ...
    Biscolab\ReCaptcha\ReCaptchaServiceProvider::class,
];
```

You can use the facade for shorter code. Add `ReCaptcha` to your aliases:

```php
'aliases' => [
    ...
    'ReCaptcha' => Biscolab\ReCaptcha\Facades\ReCaptcha::class,
];
```


## Publish package

Create `config/recaptcha.php` configuration file using the following artisan command:

```sh
$ php artisan vendor:publish --provider="Biscolab\ReCaptcha\ReCaptchaServiceProvider"
```

## Set the environment

### Add your API Keys

Open `.env` file and set `RECAPTCHA_SITE_KEY` and `RECAPTCHA_SECRET_KEY`:

```php
# in your .env file
RECAPTCHA_SITE_KEY=<YOUR_API_SITE_KEY>
RECAPTCHA_SECRET_KEY=<YOUR_API_SECRET_KEY>
RECAPTCHA_SKIP_IP=<YOUR_IP_LIST>
```

`RECAPTCHA_SKIP_IP` (since v5.2.0, not required, CSV format ) allows you to add a list of IP/CIDR (netmask included).
It will be the value of `skip_ip`

> **The following environment variables have been removed!!!**
> Now only sensitive informations as API keys are allowed as environment variables, that means you have to set configuration values in `config/recaptcha.php`

- ~~RECAPTCHA_DEFAULT_VERSION~~
- ~~RECAPTCHA_CURL_TIMEOUT~~
- ~~RECAPTCHA_DEFAULT_VALIDATION_ROUTE~~
- ~~RECAPTCHA_DEFAULT_TOKEN_PARAMETER_NAME~~
- ~~RECAPTCHA_DEFAULT_LANGUAGE~~

### Complete configuration

Open `config/recaptcha.php` configuration file and set `version`:

```php
return [
    'api_site_key'                  => env('RECAPTCHA_SITE_KEY', ''),
    'api_secret_key'                => env('RECAPTCHA_SECRET_KEY', ''),
    // changed in v4.0.0
    'version'                       => 'v2', // supported: "v3"|"v2"|"invisible"
    // @since v3.4.3 changed in v4.0.0
    'curl_timeout'                  => 10,
    'skip_ip'                       => env('RECAPTCHA_SKIP_IP', []), // array of IP addresses - String: dotted quad format e.g.: "127.0.0.1", IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
    // @since v3.2.0 changed in v4.0.0
    'default_validation_route'      => 'biscolab-recaptcha/validate',
    // @since v3.2.0 changed in v4.0.0
    'default_token_parameter_name' => 'token',
    // @since v3.6.0 changed in v4.0.0
    'default_language'             => null,
    // @since v4.0.0
    'default_form_id'              => 'biscolab-recaptcha-invisible-form', // Only for "invisible" reCAPTCHA
    // @since v4.0.0
    'explicit'                     => false, // true|false
    // @since v4.3.0
    'api_domain'                   => "www.google.com", // default value is "www.google.com"
    // @since v5.1.0
    'empty_message'                => false,
    // @since v5.1.0
    'error_message_key'            => 'validation.recaptcha',
    // @since v4.0.0
    'tag_attributes'               => [
        'theme'                    => 'light', // "light"|"dark"
        'size'                     => 'normal', // "normal"|"compact"
        'tabindex'                 => 0,
        'callback'                 => null, // DO NOT SET "biscolabOnloadCallback"
        'expired-callback'         => null, // DO NOT SET "biscolabOnloadCallback"
        'error-callback'           => null, // DO NOT SET "biscolabOnloadCallback"
    ]
];
```

| Key                                 | Type                    | Description                                                                                                                                                                                                                                                      | Default                               |
| ----------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| `api_site_key` and `api_secret_key` | `string`                | reCAPTCHA keys you have to create in order to perform Google API authentication. For more information about Site Key and Secret Key please visit [Google reCAPTCHA developer documentation](https://developers.google.com/recaptcha/docs/start)                  | `''`                                  |
| `version`                           | `string`                | indicates the reCAPTCHA version (supported: v3&#124;v2&#124;invisible). Get more info about reCAPTCHA version at <a href="https://developers.google.com/recaptcha/docs/versions" target="_blank">https://developers.google.com/recaptcha/docs/versions</a>       | `'v2'`                                |
| `curl_timeout`                      | `int`                   | the maximum number of seconds to allow cURL functions to execute                                                                                                                                                                                                 | `10`                                  |
| `skip_ip`                           | `array` &#124; `string` | a whitelist of IP addresses (array or CSV) that, if recognized, disable the reCAPTCHA validation (return always true) and if you embed JS code in blade (view) file **NO validation call will be performed**                                                     | `[]`                                  |
| `default_validation_route`          | `string`                | the route called via javascript built-in validation script (v3 only)                                                                                                                                                                                             | `'biscolab-recaptcha/validate'`       |
| `default_token_parameter_name`      | `string`                | the name of "token" GET parameter sent to `default_validation_route` to be validated (v3 only)                                                                                                                                                                   | `'token'`                             |
| `default_language`                  | `string`                | the default language code. It has no effect with v3. See [https://developers.google.com/recaptcha/docs/language](https://developers.google.com/recaptcha/docs/language) for further information                                                                  | `null`                                |
| `default_form_id`                   | `string`                | the default form ID. Only for "invisible" reCAPTCHA                                                                                                                                                                                                              | `'biscolab-recaptcha-invisible-form'` |
| `explicit`                          | `bool`                  | deferring the render can be achieved by specifying your onload callback function and adding parameters to the JavaScript resource. It has no effect with v3 and invisible (supported values: true&#124;false)                                                    | `false`                               |
| `api_domain`                        | `string`                | customize API domain. Default value is `'www.google.com'`, but, if not accessible you ca set that value to `'www.recaptcha.net'`. More info about [Can I use reCAPTCHA globally?](https://developers.google.com/recaptcha/docs/faq#can-i-use-recaptcha-globally) | `'www.google.com'`                    |
| `empty_message`                     | `bool`                  | set default error message to `null`                                                                                                                                                                                                                              | `false`                               |
| `error_message_key`                 | `string`                | set default error message translation key                                                                                                                                                                                                                        | `'validation.recaptcha'`              |

#### (array) tag_attributes

| Key                               | Type     | Description                                                                                                                                                                                                                                                          | Default    |
| --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `tag_attributes.theme`            | `string` | the color theme of the widget. (supported values: "light"&#124;"dark")                                                                                                                                                                                               | `'light'`  |
| `tag_attributes.size`             | `string` | the size of the widget. (supported values: "normal"&#124;"compact")                                                                                                                                                                                                  | `'normal'` |
| `tag_attributes.tabindex`         | `int`    | the tabindex of the widget and challenge                                                                                                                                                                                                                             | `0`        |
| `tag_attributes.callback`         | `string` | the name of your callback function, executed when the user submits a successful response. The g-recaptcha-response token is passed to your callback                                                                                                                  | `null`     |
| `tag_attributes.expired-callback` | `string` | the name of your callback function, executed when the reCAPTCHA response expires and the user needs to re-verify                                                                                                                                                     | `null`     |
| `tag_attributes.error-callback`   | `string` | the name of your callback function, executed when reCAPTCHA encounters an error (usually network connectivity) and cannot continue until connectivity is restored. If you specify a function here, you are responsible for informing the user that they should retry | `null`     |

> DO NOT SET `tag_attributes.callback`, `tag_attributes.expired-callback`, `tag_attributes.error-callback` to `biscolabOnloadCallback`. `biscolabOnloadCallback` is the default JavaScript callback function called when **explicit** is set to `true` and widget `onload` event is fired.

Here you can find further details about `tag_attributes.*` [https://developers.google.com/recaptcha/docs/display#render_param](https://developers.google.com/recaptcha/docs/display#render_param)

### Reload config cache file

> **!!! IMPORTANT !!!** Every time you change some configuration run the following shell command:

```sh
$ php artisan config:cache
```

## Have you updated?

If you are migrating from an older version check your `config/recaptcha.php` configuration file and compare it with <a href="https://github.com/biscolab/laravel-recaptcha/blob/master/config/recaptcha.php" target="_blank">https://github.com/biscolab/laravel-recaptcha/blob/master/config/recaptcha.php</a>.

> Make sure `config/recaptcha.php` is updated

## Customize error message

Just for v2 and invisible users.

Before starting please add the validation message to `resources/lang/[LANG]/validation.php` file

```php
return [
    ...
    'recaptcha' => 'Hey!!! :attribute is wrong!',
];
```

## Embed in Blade

Insert `htmlScriptTagJsApi()` helper before closing `</head>` tag.

You can also use `ReCaptcha::htmlScriptTagJsApi()`.

```blade
<!DOCTYPE html>
<html>
    <head>
        ...
        {!! htmlScriptTagJsApi($configuration) !!}
    </head>
```

#### htmlScriptTagJsApi

`htmlScriptTagJsApi` function accepts `$configuration` argument. `$configuration` has different keys depending on which ReCAPTCHA you are using:

- [Checkbox](#recaptcha-v2-checkbox)
- [Invisible](#recaptcha-v2-invisible)

### ReCAPTCHA v2 Checkbox

#### htmlScriptTagJsApi(\$configuration)

`$configuration` argument can have following keys:

- `lang` set reCAPTCHA language. This will override `default_language` in `config/recaptcha.php`. Here you ca find the complete list of availeble languages [https://developers.google.com/recaptcha/docs/language](https://developers.google.com/recaptcha/docs/language)

#### Form set-up

After you have to insert `htmlFormSnippet()` helper inside the form where you want to use the field `g-recaptcha-response`.

You can also use `ReCaptcha::htmlFormSnippet()` .

```blade
<form>
    @csrf

    ...
    {!! htmlFormSnippet() !!}
    <!-- OR -->
    {!! htmlFormSnippet($attributes) !!}
    <input type="submit">
</form>
```

> DO NOT forget `@csrf` blade directive

#### htmlFormSnippet([, array \$attributes = [] ])

`htmlFormSnippet()` function does not require attributes but you can override default config `data-` attributes:

```php
{!! htmlFormSnippet([
    "theme" => "light",
    "size" => "normal",
    "tabindex" => "3",
    "callback" => "callbackFunction",
    "expired-callback" => "expiredCallbackFunction",
    "error-callback" => "errorCallbackFunction",
]) !!}
```

`htmlFormSnippet` methos allows are only folowing attribute names:

- theme
- size
- tabindex
- callback
- expired-callback
- error-callback

> Any different attribute name will be rejected

#### Customization

In `config/recaptcha.php` you can customize reCAPTCHA widget setting `tag_attributes` array values. Take a look to `tag_attributes` section in [Complete configuration](configuration.md#complete-configuration)

### ReCAPTCHA v2 Invisible

#### htmlScriptTagJsApi(\$configuration)

`$configuration` argument can have following keys:

- `form_id` set reCAPTCHA form ID. This will override `default_form_id` in `config/recaptcha.php`. This value will be returned by `getFormId()` function in order to set the form tag `id` property.

#### Form set-up

After you have to insert `htmlFormButton($button_label, $properties)` helper inside the form where you want to use reCAPTCHA.

This function creates submit button therefore you don't have to insert `<input type="submit">` or similar.

You can also use `ReCaptcha::htmlFormButton($button_label, $properties)` .

`$button_label` is what you want to write on the submit button

```html
<form id="{{ getFormId() }}">
  @csrf ... {!! htmlFormButton($button_label, $properties) !!}
</form>
```

> DO NOT forget `@csrf` blade directive

#### getFormId()

`getFormId` function returns the default form ID value. This is the value of either `default_form_id` in `config/recaptcha.php` or `$configuration['form_id']` previously set as arguments of `htmlScriptTagJsApi` helper.

> `$configuration['form_id']` overrides default settings.

#### htmlFormButton()

`htmlFormButton` function accepts 2 arguments:

- `$button_label`: (string: optional) the button lable. For example: `Subscribe!`;
- `$properties`: (array: optional) the HTML button properties. For example:

```php
// $properties =
[
    'class' => 'btn btn-info',
    'data-foo' => 'bar'
]
```

> If `data-sitekey` and `data-callback` properties are set, they will be overwritten

> If `class` property is set the value `g-recaptcha` will be appended

## Verify submitted data

Add `recaptcha` to your rules

```php
$validator = Validator::make(request()->all(), [
    ...
    'g-recaptcha-response' => 'recaptcha',
    // OR since v4.0.0
    recaptchaFieldName() => recaptchaRuleName()
]);

// check if validator fails
if($validator->fails()) {
    ...
    $errors = $validator->errors();
}
```


## Embed in Blade

Insert `htmlScriptTagJsApi($config)` helper before closing `</head>` tag.

```html
<!DOCTYPE html>
<html>
    <head>
        ...
        {!! htmlScriptTagJsApi([
            'action' => 'homepage',
            'callback_then' => 'callbackThen',
            'callback_catch' => 'callbackCatch'
        ]) !!}

        <!-- OR! -->
        
        {!! htmlScriptTagJsApi([
            'action' => 'homepage',
            'custom_validation' => 'myCustomValidation'
        ]) !!}
    </head>
```
`$config` is required and is an associative array containing configuration parameters required for the JavaScript validation handling. 

The keys are:

| Key               | Required  | Description                                                                                                                                                           | Default value |
|-------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
| `action` 	        | no        | is the `action` parameter required by reCAPTCHA v3 API (<a href="https://developers.google.com/recaptcha/docs/v3" target="_blank">further info</a>)  	                | `homepage`    |
| `custom_validation`   | no    	| is the name of your custom callback javascript function who will override the built-in javascript validation system of this package                               | empty string  |
| `callback_then`   | no    	| (overlooked if `custom_validation`is set) is the name of your custom callback javascript function called by the built-in javascript validation system of this package in case of response success   	| empty string  |
| `callback_catch` 	| no 	    | (overlooked if `custom_validation`is set) is the name of your custom callback javascript function called by the built-in javascript validation system in this package in case of response fault 	    | empty string  |


## Built-in javascript validation system

As callback of `grecaptcha.execute` an ajax call to `config('recaptcha.default_validation_route')` will be performed using `fetch` function. In case of successful response a Promise object will be received and passed as parameter to the `callback_then` function you have set. In not set, no actions will be performed.

Same will happen with `callback_catch`. `callback_catch` will be called in event of response errors and errors will pass as parameter et that function. If not set, no actions will be performed.

Please, go to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch" target="_blank">Using Fetch</a> for further information on `fetch` javascript function.

> **Warning!!! Check browser compatibility** 
`fetch` function has compatibility issues with some browser like IE. Please create a custom validation function and set `custom_validation` with its name. That function has to accept as argument the `token`received from Google reCAPTCHA API.
>
> <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Browser_compatibility" target="_blank">Fetch browser compatibility</a> 


### Validation Laravel route

Default validation route is `config('recaptcha.default_validation_route', 'biscolab-recaptcha/validate')`.  
Route and relative Controller are built-in in the package. The route if filtered and protected by Laravel `web` Middleware, that's why is important you embed `csrf-token` HTML meta tag and send `X-Requested-Wit` and `X-CSRF-TOKEN` headers.

You can also change the validation end-point changing `default_validation_route` value in `recaptcha.php` config file.

```html
<head>
    ...
    <!-- IMPORTANT!!! remember CSRF token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">
</head>
```

### Validation response object

The output will be a JSON containing following data:

* **Default output without errors**
```json
{
    "action":"homepage",
    "challenge_ts":"2019-01-29T00:42:08Z",
    "hostname":"www.yourdomain.ext",
    "score":0.9,
    "success":true
}
```
* **Output when calling IP is included in "skip_ip" config whitelist**
```json
{
    "skip_by_ip":true,
    "score":0.9,
    "success":true
}
```
> If you embed code in your blade file using `htmlScriptTagJsApiV3` helper no validation call will be performed!
>
> More info at <a href="#complete-configuration">Configuration page</a>
* **Output with an empty response from Google reCAPTCHA API**
```json
{
    "error":"cURL response empty",
    "score":0.1,
    "success":false
}
```

In the next paragraph you can learn how handle Validation promise object

### "callback_then" and "callback_catch"

After built-in validation you should do something. How? Using `callback_then` and `callback_catch` functions.

What you have to do is just create functions and set parameters with their names.

* `callback_then` must receive one argument of type `Promise`.

* `callback_catch` must receive one argument of type `string`

The result should be something like that:

```html
<head>
    ...
    <!-- IMPORTANT!!! remember CSRF token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">
    ...
    <script type="text/javascript">
        function callbackThen(response){
        	// read HTTP status
            console.log(response.status);
            
            // read Promise object
            response.json().then(function(data){
                console.log(data);
            });
        }
        function callbackCatch(error){
            console.error('Error:', error)
        }   
    </script>    
    ...
    {!! htmlScriptTagJsApiV3([
        'action' => 'homepage',
        'callback_then' => 'callbackThen',
        'callback_catch' => 'callbackCatch'
    ]) !!}    
</head>
``` 

### "custom_validation" function

As just said you can handle validation with your own function. To do that you have to write your function and set `custom_validation` parameter with its name.

The result should be something like that:

```html
<head>
    ...
    <!-- IMPORTANT!!! remember CSRF token --> 
    <meta name="csrf-token" content="{{ csrf_token() }}">
    ...
    <script type="text/javascript">
        function myCustomValidation(token) {
            // do something with token 
        }
    </script>    
    ...
    {!! htmlScriptTagJsApiV3([
        'action' => 'homepage',
        'custom_validation' => 'myCustomValidation'
    ]) !!}    
</head>
``` 


================================================
FILE: composer.json
================================================
{
    "name": "biscolab/laravel-recaptcha",
    "description": "Simple and painless Google reCAPTCHA package for Laravel framework",
    "license": "MIT",
    "type": "library",
    "keywords": [
        "recaptcha",
        "captcha",
        "laravel",
        "validation"
    ],
    "homepage": "https://biscolab.com/laravel-recaptcha",
    "authors": [
        {
            "name": "Roberto Belotti",
            "email": "roby.belotti@gmail.com",
            "homepage": "https://biscolab.com",
            "role": "Developer"
        }
    ],
    "require": {
        "php": "^7.3|^8.0",
        "illuminate/routing": "^7.0|^8.0|^9.0|^10.0|^11.0",
        "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0"
    },
    "require-dev": {
        "orchestra/testbench": "5.*|6.*|^7.0|^8.0|^9.0",
        "phpunit/phpunit": "^9.1|^10.5"
    },
    "autoload": {
        "psr-4": {
            "Biscolab\\ReCaptcha\\": "src/"
        },
        "files": [
            "src/helpers.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Biscolab\\ReCaptcha\\Tests\\": "tests/"
        }
    },
    "scripts": {
        "test": "vendor/bin/phpunit --colors=always"
    },
    "suggest": {
        "biscolab/laravel-authlog": "It allows to handle logged-in users and force log-out if needed"
    },
    "extra": {
        "laravel": {
            "providers": [
                "Biscolab\\ReCaptcha\\ReCaptchaServiceProvider"
            ],
            "aliases": {
                "ReCaptcha": "Biscolab\\ReCaptcha\\Facades\\ReCaptcha"
            }
        }
    }
}


================================================
FILE: config/recaptcha.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - recaptcha.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

/**
 * To configure correctly please visit https://developers.google.com/recaptcha/docs/start
 */
return [

    /**
     *
     * The site key
     * get site key @ www.google.com/recaptcha/admin
     *
     */
    'api_site_key'                 => env('RECAPTCHA_SITE_KEY', ''),

    /**
     *
     * The secret key
     * get secret key @ www.google.com/recaptcha/admin
     *
     */
    'api_secret_key'               => env('RECAPTCHA_SECRET_KEY', ''),

    /**
     *
     * ReCATCHA version
     * Supported: "v2", "invisible", "v3",
     *
     * get more info @ https://developers.google.com/recaptcha/docs/versions
     *
     */
    'version'                      => 'v2',

    /**
     *
     * The curl timout in seconds to validate a recaptcha token
     * @since v3.5.0
     *
     */
    'curl_timeout'                 => 10,

    /**
     *
     * IP addresses for which validation will be skipped
     * IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
     *
     */
    'skip_ip'                      => env('RECAPTCHA_SKIP_IP', []),

    /**
     *
     * Default route called to check the Google reCAPTCHA token
     * @since v3.2.0
     *
     */
    'default_validation_route'     => 'biscolab-recaptcha/validate',

    /**
     *
     * The name of the parameter used to send Google reCAPTCHA token to verify route
     * @since v3.2.0
     *
     */
    'default_token_parameter_name' => 'token',

    /**
     *
     * The default Google reCAPTCHA language code
     * It has no effect with v3
     * @see   https://developers.google.com/recaptcha/docs/language
     * @since v3.6.0
     *
     */
    'default_language'             => null,

    /**
     *
     * The default form ID. Only for "invisible" reCAPTCHA
     * @since v4.0.0
     *
     */
    'default_form_id'              => 'biscolab-recaptcha-invisible-form',

    /**
     *
     * Deferring the render can be achieved by specifying your onload callback function and adding parameters to the JavaScript resource.
     * It has no effect with v3 and invisible
     * @see   https://developers.google.com/recaptcha/docs/display#explicit_render
     * @since v4.0.0
     * Supported true, false
     *
     */
    'explicit'                     => false,

    /**
     *
     * Set API domain. You can use "www.recaptcha.net" in case "www.google.com" is not accessible.
     * (no check will be made on the entered value)
     * @see   https://developers.google.com/recaptcha/docs/faq#can-i-use-recaptcha-globally
     * @since v4.3.0
     * Default 'www.google.com' (ReCaptchaBuilder::DEFAULT_RECAPTCHA_API_DOMAIN)
     *
     */
    'api_domain'                   => 'www.google.com',

    /**
     *
     * Set `true` when the error message must be null
     * @since v5.1.0
     * Default false
     *
     */
    'empty_message' => false,

    /**
     *
     * Set either the error message or the errom message translation key
     * @since v5.1.0
     * Default 'validation.recaptcha'
     *
     */
    'error_message_key' => 'validation.recaptcha',

    /**
     *
     * g-recaptcha tag attributes and grecaptcha.render parameters (v2 only)
     * @see   https://developers.google.com/recaptcha/docs/display#render_param
     * @since v4.0.0
     */
    'tag_attributes'               => [

        /**
         * The color theme of the widget.
         * Supported "light", "dark"
         */
        'theme'            => 'light',

        /**
         * The size of the widget.
         * Supported "normal", "compact"
         */
        'size'             => 'normal',

        /**
         * The tabindex of the widget and challenge.
         * If other elements in your page use tabindex, it should be set to make user navigation easier.
         */
        'tabindex'         => 0,

        /**
         * The name of your callback function, executed when the user submits a successful response.
         * The g-recaptcha-response token is passed to your callback.
         * DO NOT SET "biscolabOnloadCallback"
         */
        'callback'         => null,

        /**
         * The name of your callback function, executed when the reCAPTCHA response expires and the user needs to re-verify.
         * DO NOT SET "biscolabOnloadCallback"
         */
        'expired-callback' => null,

        /**
         * The name of your callback function, executed when reCAPTCHA encounters an error (usually network connectivity) and cannot continue until connectivity is restored.
         * If you specify a function here, you are responsible for informing the user that they should retry.
         * DO NOT SET "biscolabOnloadCallback"
         */
        'error-callback'   => null,
    ]
];


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
  <coverage processUncoveredFiles="true">
    <include>
      <directory suffix=".php">./src</directory>
    </include>
    <exclude>
      <directory suffix=".php">./vendor</directory>
      <directory suffix=".php">./config</directory>
    </exclude>
  </coverage>
  <testsuites>
    <testsuite name="Unit">
      <directory>./tests</directory>
    </testsuite>
  </testsuites>
</phpunit>


================================================
FILE: src/Controllers/ReCaptchaController.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaController.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 4/2/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Controllers;

use Illuminate\Routing\Controller;

/**
 * Class ReCaptchaController
 * @package Biscolab\ReCaptcha\Controllers
 */
class ReCaptchaController extends Controller
{

	/**
	 * @return array
	 */
	public function validateV3(): array
	{

		$token = request()->input(config('recaptcha.default_token_parameter_name', 'token'), '');

		return recaptcha()->validate($token);
	}
}

================================================
FILE: src/Exceptions/InvalidConfigurationException.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - InvalidConfigurationException.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 4/9/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Exceptions;

class InvalidConfigurationException extends \Exception
{

}

================================================
FILE: src/Facades/ReCaptcha.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptcha.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * Class ReCaptcha
 * @package Biscolab\ReCaptcha\Facades
 *
 * @method static string htmlScriptTagJsApi(?array $config = [])
 * @method static string htmlFormButton(?string $button_label = '', ?array $properties = [])
 * @method static string htmlFormSnippet()
 * @method static string getFormId()
 */
class ReCaptcha extends Facade
{

    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {

        return 'recaptcha';
    }
}


================================================
FILE: src/ReCaptchaBuilder.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaBuilder.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha;

use Illuminate\Support\Arr;
use Symfony\Component\HttpFoundation\IpUtils;

/**
 * Class ReCaptchaBuilder
 * @package Biscolab\ReCaptcha
 */
class ReCaptchaBuilder
{

    /**
     * @var string
     */
    const DEFAULT_API_VERSION = 'v2';

    /**
     * @var int
     */
    const DEFAULT_CURL_TIMEOUT = 10;

    /**
     * @var string
     */
    const DEFAULT_ONLOAD_JS_FUNCTION = 'biscolabOnloadCallback';

    /**
     * @var string
     */
    const DEFAULT_RECAPTCHA_RULE_NAME = 'recaptcha';

    /**
     * @var string
     */
    const DEFAULT_RECAPTCHA_FIELD_NAME = 'g-recaptcha-response';

    /**
     * @var string
     */
    const DEFAULT_RECAPTCHA_API_DOMAIN = 'www.google.com';

    /**
     * The Site key
     * please visit https://developers.google.com/recaptcha/docs/start
     * @var string
     */
    protected $api_site_key;

    /**
     * The Secret key
     * please visit https://developers.google.com/recaptcha/docs/start
     * @var string
     */
    protected $api_secret_key;

    /**
     * The chosen ReCAPTCHA version
     * please visit https://developers.google.com/recaptcha/docs/start
     * @var string
     */
    protected $version;

    /**
     * Whether is true the ReCAPTCHA is inactive
     * @var boolean
     */
    protected $skip_by_ip = false;

    /**
     * The API domain (default: retrieved from config file)
     * @var string
     */
    protected $api_domain = '';

    /**
     * The API request URI
     * @var string
     */
    protected $api_url = '';

    /**
     * The URI of the API Javascript file to embed in you pages
     * @var string
     */
    protected $api_js_url = '';

    /**
     * ReCaptchaBuilder constructor.
     *
     * @param string      $api_site_key
     * @param string      $api_secret_key
     * @param null|string $version
     */
    public function __construct(
        string $api_site_key,
        string $api_secret_key,
        ?string $version = self::DEFAULT_API_VERSION
    ) {

        $this->setApiSiteKey($api_site_key);
        $this->setApiSecretKey($api_secret_key);
        $this->setVersion($version);
        $this->setSkipByIp($this->skipByIp());
        $this->setApiDomain();
        $this->setApiUrls();
    }

    /**
     * @param string $api_site_key
     *
     * @return ReCaptchaBuilder
     */
    public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder
    {

        $this->api_site_key = $api_site_key;

        return $this;
    }

    /**
     * @param string $api_secret_key
     *
     * @return ReCaptchaBuilder
     */
    public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder
    {

        $this->api_secret_key = $api_secret_key;

        return $this;
    }

    /**
     * @return int
     */
    public function getCurlTimeout(): int
    {

        return config('recaptcha.curl_timeout', self::DEFAULT_CURL_TIMEOUT);
    }

    /**
     * @param string $version
     *
     * @return ReCaptchaBuilder
     */
    public function setVersion(string $version): ReCaptchaBuilder
    {

        $this->version = $version;

        return $this;
    }

    /**
     * @return string
     */
    public function getVersion(): string
    {

        return $this->version;
    }

    /**
     * @param bool $skip_by_ip
     *
     * @return ReCaptchaBuilder
     */
    public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder
    {

        $this->skip_by_ip = $skip_by_ip;

        return $this;
    }

    /**
     * @param null|string $api_domain
     *
     * @return ReCaptchaBuilder
     */
    public function setApiDomain(?string $api_domain = null): ReCaptchaBuilder
    {

        $this->api_domain = $api_domain ?? config('recaptcha.api_domain', self::DEFAULT_RECAPTCHA_API_DOMAIN);

        return $this;
    }

    /**
     * @return string
     */
    public function getApiDomain(): string
    {

        return $this->api_domain;
    }

    /**
     * @return ReCaptchaBuilder
     */
    public function setApiUrls(): ReCaptchaBuilder
    {

        $this->api_url = 'https://' . $this->api_domain . '/recaptcha/api/siteverify';
        $this->api_js_url = 'https://' . $this->api_domain . '/recaptcha/api.js';

        return $this;
    }

    /**
     * @return array|mixed
     */
    public function getIpWhitelist()
    {

        $whitelist = config('recaptcha.skip_ip', []);

        if (!is_array($whitelist)) {
            $whitelist = explode(',', $whitelist);
        }

        $whitelist = array_map(function ($item) {

            return trim($item);
        }, $whitelist);

        return $whitelist;
    }

    /**
     * Checks whether the user IP address is among IPs "to be skipped"
     *
     * @return boolean
     */
    public function skipByIp(): bool
    {
        return IpUtils::checkIp(request()->ip(), $this->getIpWhitelist());
    }

    /**
     * Write script HTML tag in you HTML code
     * Insert before </head> tag
     *
     * @param array|null $configuration
     *
     * @return string
     * @throws \Exception
     */
    public function htmlScriptTagJsApi(?array $configuration = []): string
    {

        $query = [];
        $html = '';

        // Language: "hl" parameter
        // resources $configuration parameter overrides default language
        $language = Arr::get($configuration, 'lang');
        if (!$language) {
            $language = config('recaptcha.default_language', null);
        }
        if ($language) {
            Arr::set($query, 'hl', $language);
        }

        // Onload JS callback function: "onload" parameter
        // "render" parameter set to "explicit"
        if (config('recaptcha.explicit', null) && $this->version === 'v2') {
            Arr::set($query, 'render', 'explicit');
            Arr::set($query, 'onload', self::DEFAULT_ONLOAD_JS_FUNCTION);

            /** @scrutinizer ignore-call */
            $html = $this->getOnLoadCallback();
        }

        // Create query string
        $query = ($query) ? '?' . http_build_query($query) : "";
        $html .= "<script src=\"" . $this->api_js_url .  $query . "\" async defer></script>";

        return $html;
    }

    /**
     * Call out to reCAPTCHA and process the response
     *
     * @param string $response
     *
     * @return boolean|array
     */
    public function validate($response)
    {

        if ($this->skip_by_ip) {
            if ($this->returnArray()) {
                // Add 'skip_by_ip' field to response
                return [
                    'skip_by_ip' => true,
                    'score'      => 0.9,
                    'success'    => true
                ];
            }

            return true;
        }

        $params = http_build_query([
            'secret'   => $this->api_secret_key,
            'remoteip' => request()->getClientIp(),
            'response' => $response,
        ]);

        $url = $this->api_url . '?' . $params;

        if (function_exists('curl_version')) {

            $curl = curl_init($url);
            curl_setopt($curl, CURLOPT_HEADER, false);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_TIMEOUT, $this->getCurlTimeout());
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            $curl_response = curl_exec($curl);
        } else {
            $curl_response = file_get_contents($url);
        }

        if (is_null($curl_response) || empty($curl_response)) {
            if ($this->returnArray()) {
                // Add 'error' field to response
                return [
                    'error'   => 'cURL response empty',
                    'score'   => 0.1,
                    'success' => false
                ];
            }

            return false;
        }
        $response = json_decode(trim($curl_response), true);

        if ($this->returnArray()) {
            return $response;
        }

        return $response['success'];
    }

    /**
     * @return string
     */
    public function getApiSiteKey(): string
    {

        return $this->api_site_key;
    }

    /**
     * @return string
     */
    public function getApiSecretKey(): string
    {

        return $this->api_secret_key;
    }

    /**
     * @return bool
     */
    protected function returnArray(): bool
    {

        return ($this->version == 'v3');
    }

    /**
     * @return string
     */
    public function getOnLoadCallback(): string
    {

        return "";
    }
}


================================================
FILE: src/ReCaptchaBuilderInvisible.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaBuilderInvisible.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha;

use Biscolab\ReCaptcha\Exceptions\InvalidConfigurationException;
use Illuminate\Support\Arr;

/**
 * Class ReCaptchaBuilderInvisible
 * @package Biscolab\ReCaptcha
 */
class ReCaptchaBuilderInvisible extends ReCaptchaBuilder
{

    /**
     * @var null|string
     */
    protected $form_id = null;

    /**
     * ReCaptchaBuilderInvisible constructor.
     *
     * @param string $api_site_key
     * @param string $api_secret_key
     */
    public function __construct(string $api_site_key, string $api_secret_key)
    {

        parent::__construct($api_site_key, $api_secret_key, 'invisible');
    }

    /**
     * Write HTML <button> tag in your HTML code
     * Insert before </form> tag
     *
     * @param string     $button_label
     * @param array|null $properties
     *
     * @return string
     */
    public function htmlFormButton($button_label = 'Submit', ?array $properties = []): string
    {

        $tag_properties = '';

        $properties = array_merge([
            'data-callback' => 'biscolabLaravelReCaptcha',
        ], $properties, 
        [
            'data-sitekey'  => $this->api_site_key
        ]);

        if (empty($properties['class'])) {
            $properties['class'] = 'g-recaptcha';
        } else {
            $properties['class'] .= ' g-recaptcha';
        }

        ksort($properties);

        if ($properties) {
//            $tag_properties = str_replace("=", '="',
//                    http_build_query($properties, null, '" ', PHP_QUERY_RFC3986)) . '"';
            $temp_properties = [];
            foreach ($properties as $k => $v) {
                $temp_properties[] = $k . '="' . $v . '"';
            }

            $tag_properties = implode(" ", $temp_properties);
        }

        return ($this->version == 'invisible') ? '<button ' . $tag_properties . '>' . $button_label . '</button>' : '';
    }

    /**
     * Write script HTML tag in you HTML code
     * Insert before </head> tag
     *
     * @param array|null $configuration
     *
     * @return string
     * @throws \Exception
     */
    public function htmlScriptTagJsApi(?array $configuration = []): string
    {

        $html = parent::htmlScriptTagJsApi();

        $form_id = Arr::get($configuration, 'form_id');
        if (!$form_id) {
            $form_id = $this->getFormId();
        } else {
            $this->form_id = $form_id;
        }
        $html .= '<script>
		       function biscolabLaravelReCaptcha(token) {
		         document.getElementById("' . $form_id . '").submit();
		       }
		     </script>';

        return $html;
    }

    /**
     * @return string
     * @throws \Exception
     */
    public function getFormId(): string
    {

        if (!$this->form_id) {
            $form_id = config('recaptcha.default_form_id');
        } else {
            $form_id = $this->form_id;
        }
        if (!$form_id) {
            throw new InvalidConfigurationException("formId required");
        }

        return $form_id;
    }
}



================================================
FILE: src/ReCaptchaBuilderV2.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaBuilderV2.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha;

use Biscolab\ReCaptcha\Exceptions\InvalidConfigurationException;
use Illuminate\Support\Arr;

/**
 * Class ReCaptchaBuilderV2
 * @package Biscolab\ReCaptcha
 */
class ReCaptchaBuilderV2 extends ReCaptchaBuilder
{

    protected static $allowed_data_attribute = [
        "theme",
        "size",
        "tabindex",
        "callback",
        "expired-callback",
        "error-callback",
    ];

    /**
     * ReCaptchaBuilderV2 constructor.
     *
     * @param string $api_site_key
     * @param string $api_secret_key
     */
    public function __construct(string $api_site_key, string $api_secret_key)
    {

        parent::__construct($api_site_key, $api_secret_key, 'v2');
    }

    /**
     * Write ReCAPTCHA HTML tag in your FORM
     * Insert before </form> tag
     * 
     * @param null|array $attributes
     * @return string
     */
    public function htmlFormSnippet(?array $attributes = []): string
    {

        $data_attributes = [];
        $config_data_attributes = array_merge($this->getTagAttributes(), self::cleanAttributes($attributes));
        ksort($config_data_attributes);
        foreach ($config_data_attributes as $k => $v) {
            if ($v) {
                $data_attributes[] = 'data-' . $k . '="' . $v . '"';
            }
        }

        $html = '<div class="g-recaptcha" ' . implode(" ", $data_attributes) . ' id="recaptcha-element"></div>';

        return $html;
    }

    /**
     * @return array
     * @throws InvalidConfigurationException
     */
    public function getTagAttributes(): array
    {

        $tag_attributes = [
            'sitekey' => $this->api_site_key
        ];

        $tag_attributes = array_merge($tag_attributes, config('recaptcha.tag_attributes', []));

        if (Arr::get($tag_attributes, 'callback') === ReCaptchaBuilder::DEFAULT_ONLOAD_JS_FUNCTION) {
            throw new InvalidConfigurationException('Property "callback" ("data-callback") must be different from "' . ReCaptchaBuilder::DEFAULT_ONLOAD_JS_FUNCTION . '"');
        }

        if (Arr::get($tag_attributes, 'expired-callback') === ReCaptchaBuilder::DEFAULT_ONLOAD_JS_FUNCTION) {
            throw new InvalidConfigurationException('Property "expired-callback" ("data-expired-callback") must be different from "' . ReCaptchaBuilder::DEFAULT_ONLOAD_JS_FUNCTION . '"');
        }

        if (Arr::get($tag_attributes, 'error-callback') === ReCaptchaBuilder::DEFAULT_ONLOAD_JS_FUNCTION) {
            throw new InvalidConfigurationException('Property "error-callback" ("data-error-callback") must be different from "' . ReCaptchaBuilder::DEFAULT_ONLOAD_JS_FUNCTION . '"');
        }

        return $tag_attributes;
    }

    /**
     * @return string
     */
    public function getOnLoadCallback(): string
    {

        $attributes = $this->getTagAttributes();

        return "<script>var biscolabOnloadCallback = function() {grecaptcha.render('recaptcha-element', " . json_encode($attributes) . ");};</script>";
    }

    /**
     * Compare given attributes with allowed attributes
     *
     * @param array|null $attributes
     * @return array
     */
    public static function cleanAttributes(?array $attributes = []): array
    {
        return array_filter($attributes, function ($k) {
            return in_array($k, self::$allowed_data_attribute);
        }, ARRAY_FILTER_USE_KEY);
    }
}


================================================
FILE: src/ReCaptchaBuilderV3.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaBuilderV3.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 22/1/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha;

use Illuminate\Support\Arr;

/**
 * Class ReCaptchaBuilderV3
 * @package Biscolab\ReCaptcha
 */
class ReCaptchaBuilderV3 extends ReCaptchaBuilder
{

    /**
     * ReCaptchaBuilderV3 constructor.
     *
     * @param string $api_site_key
     * @param string $api_secret_key
     */
    public function __construct(string $api_site_key, string $api_secret_key)
    {

        parent::__construct($api_site_key, $api_secret_key, 'v3');
    }

    public function getTokenParameterName(): string
    {
        return config(
            'recaptcha.default_token_parameter_name',
            'token'
        );
    }

    public function getValidationUrl(): string
    {
        return url(config(
            'recaptcha.default_validation_route',
            'biscolab-recaptcha/validate'
        ));
    }

    public function getValidationUrlWithToken(): string
    {
        return implode(
            "?",
            [
                $this->getValidationUrl(),
                $this->getTokenParameterName()
            ]
        );
    }

    /**
     * Write script HTML tag in you HTML code
     * Insert before </head> tag
     *
     * @param array|null $configuration
     *
     * @return string
     */
    public function htmlScriptTagJsApi(?array $configuration = []): string
    {

        if ($this->skip_by_ip) {
            return '';
        }

        $html = "<script src=\"" . $this->api_js_url . "?render={$this->api_site_key}\"></script>";

        $action = Arr::get($configuration, 'action', 'homepage');

        $js_custom_validation = Arr::get($configuration, 'custom_validation', '');

        // Check if set custom_validation. That function will override default fetch validation function
        if ($js_custom_validation) {

            $validate_function = ($js_custom_validation) ? "{$js_custom_validation}(token);" : '';
        } else {

            $js_then_callback = Arr::get($configuration, 'callback_then', '');
            $js_callback_catch = Arr::get($configuration, 'callback_catch', '');

            $js_then_callback = ($js_then_callback) ? "{$js_then_callback}(response)" : '';
            $js_callback_catch = ($js_callback_catch) ? "{$js_callback_catch}(err)" : '';

            $validate_function = "
                fetch('" . $this->getValidationUrlWithToken() . "=' + token, {
                    headers: {
                        \"X-Requested-With\": \"XMLHttpRequest\",
                        \"X-CSRF-TOKEN\": csrfToken.content
                    }
                })
                .then(function(response) {
                   	{$js_then_callback}
                })
                .catch(function(err) {
                    {$js_callback_catch}
                });";
        }

        $html .= "<script>
                    var csrfToken = document.head.querySelector('meta[name=\"csrf-token\"]');
                  grecaptcha.ready(function() {
                      grecaptcha.execute('{$this->api_site_key}', {action: '{$action}'}).then(function(token) {
                        {$validate_function}
                      });
                  });
		     </script>";

        return $html;
    }
}


================================================
FILE: src/ReCaptchaServiceProvider.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaServiceProvider.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;

/**
 * Class ReCaptchaServiceProvider
 * @package Biscolab\ReCaptcha
 */
class ReCaptchaServiceProvider extends ServiceProvider
{

    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     *
     */
    public function boot()
    {

        $this->addValidationRule();
        $this->registerRoutes();
        $this->publishes([
            __DIR__ . '/../config/recaptcha.php' => config_path('recaptcha.php'),
        ], 'config');
    }

    /**
     * Extends Validator to include a recaptcha type
     */
    public function addValidationRule()
    {
        $message = null;

        if (!config('recaptcha.empty_message')) {
            $message = trans(config('recaptcha.error_message_key'));
        }
        Validator::extendImplicit(recaptchaRuleName(), function ($attribute, $value) {

            return app('recaptcha')->validate($value);
        }, $message);
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {

        $this->mergeConfigFrom(
            __DIR__ . '/../config/recaptcha.php',
            'recaptcha'
        );

        $this->registerReCaptchaBuilder();
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides(): array
    {

        return ['recaptcha'];
    }

    /**
     * @return ReCaptchaServiceProvider
     *
     * @since v3.4.1
     */
    protected function registerRoutes(): ReCaptchaServiceProvider
    {

        Route::get(
            config('recaptcha.default_validation_route', 'biscolab-recaptcha/validate'),
            ['uses' => 'Biscolab\ReCaptcha\Controllers\ReCaptchaController@validateV3']
        )->middleware('web');

        return $this;
    }

    /**
     * Register the HTML builder instance.
     *
     * @return void
     */
    protected function registerReCaptchaBuilder()
    {

        $this->app->singleton('recaptcha', function ($app) {

            $recaptcha_class = '';

            switch (config('recaptcha.version')) {
                case 'v3':
                    $recaptcha_class = ReCaptchaBuilderV3::class;
                    break;
                case 'v2':
                    $recaptcha_class = ReCaptchaBuilderV2::class;
                    break;
                case 'invisible':
                    $recaptcha_class = ReCaptchaBuilderInvisible::class;
                    break;
            }

            return new $recaptcha_class(config('recaptcha.api_site_key'), config('recaptcha.api_secret_key'));
        });
    }
}


================================================
FILE: src/helpers.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - helpers.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

use Biscolab\ReCaptcha\Facades\ReCaptcha;

if (!function_exists('recaptcha')) {
    /**
     * @return Biscolab\ReCaptcha\ReCaptchaBuilder|\Biscolab\ReCaptcha\ReCaptchaBuilderV2|\Biscolab\ReCaptcha\ReCaptchaBuilderInvisible|\Biscolab\ReCaptcha\ReCaptchaBuilderV3
     */
    function recaptcha(): \Biscolab\ReCaptcha\ReCaptchaBuilder
    {

        return app('recaptcha');
    }
}

/**
 * call ReCaptcha::htmlScriptTagJsApi()
 * Write script HTML tag in you HTML code
 * Insert before </head> tag
 *
 * @param $config ['form_id'] required if you are using invisible ReCaptcha
 */
if (!function_exists('htmlScriptTagJsApi')) {

    /**
     * @param array|null $config
     *
     * @return string
     */
    function htmlScriptTagJsApi(?array $config = []): string
    {

        return ReCaptcha::htmlScriptTagJsApi($config);
    }
}

/**
 * call ReCaptcha::htmlFormButton()
 * Write HTML <button> tag in your HTML code
 * Insert before </form> tag
 *
 * Warning! Using only with ReCAPTCHA INVISIBLE
 *
 * @param $buttonInnerHTML What you want to write on the submit button
 */
if (!function_exists('htmlFormButton')) {

    /**
     * @param null|string $button_label
     * @param array|null  $properties
     *
     * @return string
     */
    function htmlFormButton(?string $button_label = 'Submit', ?array $properties = []): string
    {

        return ReCaptcha::htmlFormButton($button_label, $properties);
    }
}

/**
 * call ReCaptcha::htmlFormSnippet()
 * Write ReCAPTCHA HTML tag in your FORM
 * Insert before </form> tag
 *
 * Warning! Using only with ReCAPTCHA v2
 */
if (!function_exists('htmlFormSnippet')) {

    /**
     * @param null|array $attributes
     * @return string
     */
    function htmlFormSnippet(?array $attributes = []): string
    {

        return ReCaptcha::htmlFormSnippet($attributes);
    }
}

/**
 * call ReCaptcha::getFormId()
 * return the form ID
 * Warning! Using only with ReCAPTCHA invisible
 */
if (!function_exists('getFormId')) {

    /**
     * @return string
     */
    function getFormId(): string
    {

        return ReCaptcha::getFormId();
    }
}

/**
 * return ReCaptchaBuilder::DEFAULT_RECAPTCHA_RULE_NAME value ("recaptcha")
 * Use V2 (checkbox and invisible)
 */
if (!function_exists('recaptchaRuleName')) {

    /**
     * @return string
     */
    function recaptchaRuleName(): string
    {

        return \Biscolab\ReCaptcha\ReCaptchaBuilder::DEFAULT_RECAPTCHA_RULE_NAME;
    }
}

/**
 * return ReCaptchaBuilder::DEFAULT_RECAPTCHA_FIELD_NAME value "g-recaptcha-response"
 * Use V2 (checkbox and invisible)
 */
if (!function_exists('recaptchaFieldName')) {

    /**
     * @return string
     */
    function recaptchaFieldName(): string
    {

        return \Biscolab\ReCaptcha\ReCaptchaBuilder::DEFAULT_RECAPTCHA_FIELD_NAME;
    }
}


================================================
FILE: tests/ReCaptchaConfigurationTest.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaConfigurationTest.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 13/2/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\ReCaptchaBuilder;
use Biscolab\ReCaptcha\ReCaptchaBuilderV2;

/**
 * Class ReCaptchaConfigurationTest
 * @package Biscolab\ReCaptcha\Tests
 */
class ReCaptchaConfigurationTest extends TestCase
{

	/**
	 * @var ReCaptchaBuilder
	 */
	protected $recaptcha;

	/**
	 * @test
	 */
	public function testGetApiSiteKey() {
		$this->assertEquals("api_site_key", $this->recaptcha->getApiSiteKey());
	}

	/**
	 * @test
	 */
	public function testGetApiSecretKey() {
		$this->assertEquals("api_secret_key", $this->recaptcha->getApiSecretKey());
	}

	/**
	 * @test
	 */
	public function testSkipIpWhiteListIsArray()
	{

		$ip_whitelist = $this->recaptcha->getIpWhitelist();
		$this->assertTrue(is_array($ip_whitelist));
		$this->assertCount(2, $ip_whitelist);

		$this->assertEquals('10.0.0.1', $ip_whitelist[0]);
		$this->assertEquals('10.0.0.2', $ip_whitelist[1]);
	}

	/**
	 * @test
	 */
	public function testCurlTimeoutIsSet()
	{

		$this->assertEquals(3, $this->recaptcha->getCurlTimeout());
	}

	/**
	 * Define environment setup.
	 *
	 * @param  \Illuminate\Foundation\Application $app
	 *
	 * @return void
	 */
	protected function getEnvironmentSetUp($app)
	{

		$app['config']->set('recaptcha.api_site_key', 'api_site_key');
		$app['config']->set('recaptcha.api_secret_key', 'api_secret_key');
		$app['config']->set('recaptcha.skip_ip', '10.0.0.1,10.0.0.2');
		$app['config']->set('recaptcha.curl_timeout', 3);
	}

	/**
	 * Setup the test environment.
	 */
	protected function setUp(): void
	{

		parent::setUp(); // TODO: Change the autogenerated stub

		$this->recaptcha = recaptcha();
	}
}

================================================
FILE: tests/ReCaptchaCustomApiDomainTest.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaCustomApiDomainTest.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 13/9/2020
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\ReCaptchaBuilderInvisible;
use Biscolab\ReCaptcha\ReCaptchaBuilderV2;
use Biscolab\ReCaptcha\ReCaptchaBuilderV3;

class ReCaptchaCustomApiDomainTest extends TestCase
{

    /**
     * @var ReCaptchaBuilderInvisible
     */
    protected $recaptcha_invisible;

    /**
     * @var ReCaptchaBuilderV2
     */
    protected $recaptcha_v2;

    /**
     * @var ReCaptchaBuilderV3
     */
    protected $recaptcha_v3;

    /**
     * @test
     */
    public function testRecaptchaApiDomainChangesByConfig()
    {
        $this->app['config']->set('recaptcha.api_domain', 'www.recaptcha.net');
        $this->assertEquals("www.recaptcha.net", $this->recaptcha_v2->getApiDomain());
        $this->assertEquals("www.recaptcha.net", $this->recaptcha_invisible->getApiDomain());
        $this->assertEquals("www.recaptcha.net", $this->recaptcha_v3->getApiDomain());
    }

    /**
     * @test
     */
    public function testRecaptchaApiDomainChangesByConfigInHtmlScriptTagJsApi()
    {
        $this->assertStringContainsString("https://www.recaptcha.net/recaptcha/api.js", $this->recaptcha_v2->htmlScriptTagJsApi());
        $this->assertStringContainsString("https://www.recaptcha.net/recaptcha/api.js", $this->recaptcha_invisible->htmlScriptTagJsApi());
        $this->assertStringContainsString("https://www.recaptcha.net/recaptcha/api.js", $this->recaptcha_v3->htmlScriptTagJsApi());
    }

    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application $app
     *
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {

        $app['config']->set('recaptcha.api_domain', 'www.recaptcha.net');
    }

    /**
     * @inheritdoc
     */
    protected function setUp(): void
    {

        parent::setUp(); // TODO: Change the autogenerated stub
        $this->recaptcha_invisible = new ReCaptchaBuilderInvisible('api_site_key', 'api_secret_key');
        $this->recaptcha_v2 = new ReCaptchaBuilderV2('api_site_key', 'api_secret_key');
        $this->recaptcha_v3 = new ReCaptchaBuilderV3('api_site_key', 'api_secret_key');
    }
}


================================================
FILE: tests/ReCaptchaHelpersInvisibleTest.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaHelpersInvisibleTest.phpp
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 8/8/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\Facades\ReCaptcha;

class ReCaptchaHelpersInvisibleTest extends TestCase
{

    /**
     * @test
     */
    public function testHtmlScriptTagJsApiCalledByFacade()
    {

        ReCaptcha::shouldReceive('htmlScriptTagJsApi')
            ->once()
            ->with(["form_id" => "test-form"]);

        htmlScriptTagJsApi(["form_id" => "test-form"]);
    }

    /**
     * @test
     */
    public function testHtmlFormButtonCalledByFacade()
    {

        ReCaptcha::shouldReceive('htmlFormButton')
            ->once()
            ->with("Inner text", ['id' => 'button_id']);

        htmlFormButton("Inner text", ['id' => 'button_id']);
    }

    /**
     * @test
     */
    public function testGetFormIdCalledByFacade()
    {

        ReCaptcha::shouldReceive('getFormId')
            ->once();

        getFormId();
    }

    public function testHtmlFormButtonConfiguration()
    {
        $button_html = htmlFormButton("Inner text", ['id' => 'button_id', 'class' => 'button_class', 'data-sitekey' => 'custom-data-sitekey', 'data-callback' => 'myCallback']);

        $this->assertEquals('<button class="button_class g-recaptcha" data-callback="myCallback" data-sitekey="api_site_key" id="button_id">Inner text</button>', $button_html);
    }

    /**
     * @test
     * @expectedException \TypeError
     */
    public function testHtmlFormSnippetCalledByFacade()
    {

        $this->expectException('\TypeError');
        ReCaptcha::shouldReceive('htmlFormSnippet')
            ->once();

        htmlFormSnippet();
    }

    public function testGetFormIdReturnDefaultFormIdValue()
    {
        $this->assertEquals('biscolab-recaptcha-invisible-form', getFormId());
    }

    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application $app
     *
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {

        $app['config']->set('recaptcha.api_site_key', 'api_site_key');
        $app['config']->set('recaptcha.api_site_key', 'api_site_key');
        $app['config']->set('recaptcha.version', 'invisible');
    }
}


================================================
FILE: tests/ReCaptchaHelpersV2ExplicitTest.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaHelpersV2ExplicitTest.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 2/9/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\ReCaptchaBuilderV2;

class ReCaptchaHelpersV2ExplicitTest extends TestCase
{

    /**
     * @test
     */
    public function testGetOnLoadCallbackFunction()
    {

        $recaptcha = \recaptcha();
        /** @scrutinizer ignore-call */
        $callback = $recaptcha->getOnLoadCallback();

        $this->assertEquals(
            '<script>var biscolabOnloadCallback = function() {grecaptcha.render(\'recaptcha-element\', {"sitekey":"api_site_key","theme":"dark","size":"compact","tabindex":"2","callback":"callbackFunction","expired-callback":"expiredCallbackFunction","error-callback":"errorCallbackFunction"});};</script>',
            $callback
        );
    }

    /**
     * @test
     */
    public function testHtmlScriptTagJsApiHasJavascriptRenderFunction()
    {

        $html = htmlScriptTagJsApi();

        $this->assertEquals(
            '<script>var biscolabOnloadCallback = function() {grecaptcha.render(\'recaptcha-element\', {"sitekey":"api_site_key","theme":"dark","size":"compact","tabindex":"2","callback":"callbackFunction","expired-callback":"expiredCallbackFunction","error-callback":"errorCallbackFunction"});};</script><script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=biscolabOnloadCallback" async defer></script>',
            $html
        );
    }

    /**
     * @test
     */
    public function testTagAttributes()
    {

        $recaptcha = \recaptcha();
        /** @scrutinizer ignore-call */
        $tag_attributes = $recaptcha->getTagAttributes();

        $this->assertArrayHasKey('sitekey', $tag_attributes);
        $this->assertArrayHasKey('theme', $tag_attributes);
        $this->assertArrayHasKey('size', $tag_attributes);
        $this->assertArrayHasKey('tabindex', $tag_attributes);
        $this->assertArrayHasKey('callback', $tag_attributes);
        $this->assertArrayHasKey('expired-callback', $tag_attributes);
        $this->assertArrayHasKey('error-callback', $tag_attributes);

        $this->assertEquals($tag_attributes['sitekey'], 'api_site_key');
        $this->assertEquals($tag_attributes['theme'], 'dark');
        $this->assertEquals($tag_attributes['size'], 'compact');
        $this->assertEquals($tag_attributes['tabindex'], '2');
        $this->assertEquals($tag_attributes['callback'], 'callbackFunction');
        $this->assertEquals($tag_attributes['expired-callback'], 'expiredCallbackFunction');
        $this->assertEquals($tag_attributes['error-callback'], 'errorCallbackFunction');
    }

    /**
     * @test
     */
    public function testExpectReCaptchaInstanceOfReCaptchaBuilderV2()
    {

        $this->assertInstanceOf(ReCaptchaBuilderV2::class, \recaptcha());
    }

    /**
     * @test
     */
    public function testHtmlFormSnippet()
    {

        /** @scrutinizer ignore-call */
        $html_snippet = \recaptcha()->htmlFormSnippet();
        $this->assertEquals(
            '<div class="g-recaptcha" data-callback="callbackFunction" data-error-callback="errorCallbackFunction" data-expired-callback="expiredCallbackFunction" data-sitekey="api_site_key" data-size="compact" data-tabindex="2" data-theme="dark" id="recaptcha-element"></div>',
            $html_snippet
        );
    }

    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application $app
     *
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {

        $app['config']->set('recaptcha.api_site_key', 'api_site_key');
        $app['config']->set('recaptcha.version', 'v2');
        $app['config']->set('recaptcha.explicit', true);
        $app['config']->set('recaptcha.tag_attributes', [
            'theme'            => 'dark',
            'size'             => 'compact',
            'tabindex'         => '2',
            'callback'         => 'callbackFunction',
            'expired-callback' => 'expiredCallbackFunction',
            'error-callback'   => 'errorCallbackFunction',
        ]);
    }
}


================================================
FILE: tests/ReCaptchaHelpersV2Test.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaHelpersV2Test.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 8/8/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\Facades\ReCaptcha;
use Biscolab\ReCaptcha\ReCaptchaBuilderV2;

class ReCaptchaHelpersV2Test extends TestCase
{

    /**
     * @test
     */
    public function testHtmlScriptTagJsApiCalledByFacade()
    {

        ReCaptcha::shouldReceive('htmlScriptTagJsApi')
            ->once()
            ->with(["key" => "val"]);

        htmlScriptTagJsApi(["key" => "val"]);
    }

    /**
     * @test
     */
    public function testHtmlFormSnippetCalledByFacade()
    {

        ReCaptcha::shouldReceive('htmlFormSnippet')
            ->once();

        htmlFormSnippet();
    }

    /**
     * @test
     */
    public function testTagAttributes()
    {

        $recaptcha = \recaptcha();

        $tag_attributes = $recaptcha->getTagAttributes();

        $this->assertArrayHasKey('sitekey', $recaptcha->getTagAttributes());
        $this->assertArrayHasKey('theme', $recaptcha->getTagAttributes());
        $this->assertArrayHasKey('size', $tag_attributes);
        $this->assertArrayHasKey('tabindex', $tag_attributes);
        $this->assertArrayHasKey('callback', $tag_attributes);
        $this->assertArrayHasKey('expired-callback', $tag_attributes);
        $this->assertArrayHasKey('error-callback', $tag_attributes);

        $this->assertEquals($tag_attributes['sitekey'], 'api_site_key');
        $this->assertEquals($tag_attributes['theme'], 'dark');
        $this->assertEquals($tag_attributes['size'], 'compact');
        $this->assertEquals($tag_attributes['tabindex'], '2');
        $this->assertEquals($tag_attributes['callback'], 'callbackFunction');
        $this->assertEquals($tag_attributes['expired-callback'], 'expiredCallbackFunction');
        $this->assertEquals($tag_attributes['error-callback'], 'errorCallbackFunction');
    }

    /**
     * @test
     */
    public function testExpectReCaptchaInstanceOfReCaptchaBuilderV2()
    {

        $this->assertInstanceOf(ReCaptchaBuilderV2::class, \recaptcha());
    }

    /**
     * @expectedException     \Error
     */
    public function testExpectExceptionWhenGetFormIdFunctionIsCalled()
    {
        $this->expectException('\Error');
        getFormId();
    }

    /**
     * @test
     */
    public function testHtmlFormSnippet()
    {

        $html_snippet = \recaptcha()->htmlFormSnippet();
        $this->assertEquals(
            '<div class="g-recaptcha" data-callback="callbackFunction" data-error-callback="errorCallbackFunction" data-expired-callback="expiredCallbackFunction" data-sitekey="api_site_key" data-size="compact" data-tabindex="2" data-theme="dark" id="recaptcha-element"></div>',
            $html_snippet
        );
    }

    /**
     * @test
     */
    public function testHtmlFormSnippetOverridesDefaultAttributes()
    {

        $html_snippet = \recaptcha()->htmlFormSnippet([
            "theme" => "light",
            "size" => "normal",
            "tabindex" => "3",
            "callback" => "callbackFunctionNew",
            "expired-callback" => "expiredCallbackFunctionNew",
            "error-callback" => "errorCallbackFunctionNew",
            "not-allowed-attribute" => "error",
        ]);
        $this->assertEquals(
            '<div class="g-recaptcha" data-callback="callbackFunctionNew" data-error-callback="errorCallbackFunctionNew" data-expired-callback="expiredCallbackFunctionNew" data-sitekey="api_site_key" data-size="normal" data-tabindex="3" data-theme="light" id="recaptcha-element"></div>',
            $html_snippet
        );
    }

    /**
     * @test
     */
    public function testCleanAttributesReturnsOnlyAllowedAttributes()
    {
        $attributes = ReCaptchaBuilderV2::cleanAttributes([
            "theme" => "theme",
            "size" => "size",
            "tabindex" => "tabindex",
            "callback" => "callback",
            "expired-callback" => "expired-callback",
            "error-callback" => "error-callback",
            "not-allowed-attribute" => "error",
        ]);
        $this->assertArrayHasKey("theme", $attributes);
        $this->assertArrayHasKey("size", $attributes);
        $this->assertArrayHasKey("tabindex", $attributes);
        $this->assertArrayHasKey("callback", $attributes);
        $this->assertArrayHasKey("expired-callback", $attributes);
        $this->assertArrayHasKey("error-callback", $attributes);
        $this->assertArrayNotHasKey("not-allowed-attribute", $attributes);
    }

    /**
     * @test
     */
    public function testHtmlFormSnippetKeepsDefaultConfigValuesUnlessOtherwiseStated()
    {
        $html_snippet = \recaptcha()->htmlFormSnippet([
            'callback'         => 'callbackFunction',
            'expired-callback' => 'expiredCallbackFunction',
            'error-callback'   => 'errorCallbackFunction',
        ]);
        $this->assertEquals(
            '<div class="g-recaptcha" data-callback="callbackFunction" data-error-callback="errorCallbackFunction" data-expired-callback="expiredCallbackFunction" data-sitekey="api_site_key" data-size="compact" data-tabindex="2" data-theme="dark" id="recaptcha-element"></div>',
            $html_snippet
        );
    }

    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application $app
     *
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {

        $app['config']->set('recaptcha.api_site_key', 'api_site_key');
        $app['config']->set('recaptcha.version', 'v2');
        $app['config']->set('recaptcha.tag_attributes', [
            'theme'            => 'dark',
            'size'             => 'compact',
            'tabindex'         => '2',
            'callback'         => 'callbackFunction',
            'expired-callback' => 'expiredCallbackFunction',
            'error-callback'   => 'errorCallbackFunction',
        ]);
    }
}


================================================
FILE: tests/ReCaptchaInvalidConfigurationTest.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaInvalidConfigurationTest.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 4/9/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\Exceptions\InvalidConfigurationException;
use Biscolab\ReCaptcha\ReCaptchaBuilder;

/**
 * Class ReCaptchaInvalidConfigurationTest
 * @package Biscolab\ReCaptcha\Tests
 */
class ReCaptchaInvalidConfigurationTest extends TestCase
{

    /**
     * @test
     */
    public function testV2HtmlScriptTagJsApiThrowsInvalidConfigurationException()
    {

        $this->expectException(InvalidConfigurationException::class);

        htmlScriptTagJsApi();
    }

    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application $app
     *
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {

        $app['config']->set('recaptcha.api_site_key', 'api_site_key');
        $app['config']->set('recaptcha.api_secret_key', 'api_secret_key');
        $app['config']->set('recaptcha.explicit', true);
        $app['config']->set('recaptcha.tag_attributes.callback', ReCaptchaBuilder::DEFAULT_ONLOAD_JS_FUNCTION);
    }

}

================================================
FILE: tests/ReCaptchaLangTest.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaLangTest.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 7/8/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\Facades\ReCaptcha;
use Biscolab\ReCaptcha\ReCaptchaBuilderInvisible;
use Biscolab\ReCaptcha\ReCaptchaBuilderV2;

class ReCaptchaLangTest extends TestCase
{

	/**
	 * @var ReCaptchaBuilderInvisible
	 */
	protected $recaptcha_invisible;

	/**
	 * @var ReCaptchaBuilderV2
	 */
	protected $recaptcha_v2;

	/**
	 * @tests
	 */
	public function testHtmlScriptTagJsApiGetHtmlScriptWithHlParam()
	{

		$r = ReCaptcha::htmlScriptTagJsApi();
		$this->assertEquals('<script src="https://www.google.com/recaptcha/api.js?hl=it" async defer></script>', $r);
	}

	/**
	 * @tests
	 */
	public function testHtmlScriptTagJsApiGetHtmlScriptOverridingHlParam()
	{

		$r = ReCaptcha::htmlScriptTagJsApi(['lang' => 'en']);
		$this->assertEquals('<script src="https://www.google.com/recaptcha/api.js?hl=en" async defer></script>', $r);
	}

	/**
	 * Define environment setup.
	 *
	 * @param  \Illuminate\Foundation\Application $app
	 *
	 * @return void
	 */
	protected function getEnvironmentSetUp($app)
	{

		$app['config']->set('recaptcha.default_language', 'it');
	}

	/**
	 * Setup the test environment.
	 */
	protected function setUp(): void
	{

		parent::setUp(); // TODO: Change the autogenerated stub

		$this->recaptcha_invisible = new ReCaptchaBuilderInvisible('api_site_key', 'api_secret_key');
		$this->recaptcha_v2 = new ReCaptchaBuilderV2('api_site_key', 'api_secret_key');

	}
}

================================================
FILE: tests/ReCaptchaTest.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaTest.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\Facades\ReCaptcha;
use Biscolab\ReCaptcha\ReCaptchaBuilder;
use Biscolab\ReCaptcha\ReCaptchaBuilderInvisible;
use Biscolab\ReCaptcha\ReCaptchaBuilderV2;
use Biscolab\ReCaptcha\ReCaptchaBuilderV3;

/**
 * Class ReCaptchaTest
 * @package Biscolab\ReCaptcha\Tests
 */
class ReCaptchaTest extends TestCase
{

	/**
	 * @var ReCaptchaBuilderInvisible
	 */
	protected $recaptcha_invisible = null;

	/**
	 * @var ReCaptchaBuilderV2
	 */
	protected $recaptcha_v2 = null;

	/**
	 * @var ReCaptchaBuilderV3
	 */
	protected $recaptcha_v3 = null;

	/**
	 * @tests
	 */
	public function testHtmlScriptTagJsApiGetHtmlScriptTag()
	{

		$r = ReCaptcha::htmlScriptTagJsApi();
		$this->assertEquals('<script src="https://www.google.com/recaptcha/api.js" async defer></script>', $r);
	}

	/**
	 * @test
	 */
	public function testReCaptchaInvisibleHtmlFormButtonDefault()
	{

		$recaptcha = $this->recaptcha_invisible;
		$html_button = $recaptcha->htmlFormButton();
		$this->assertEquals(
			'<button class="g-recaptcha" data-callback="biscolabLaravelReCaptcha" data-sitekey="api_site_key">Submit</button>',
			$html_button
		);
	}

	/**
	 * @test
	 */
	public function testReCaptchaInvisibleHtmlFormButtonCustom()
	{

		$recaptcha = $this->recaptcha_invisible;
		$html_button = $recaptcha->htmlFormButton('Custom Text');
		$this->assertEquals(
			'<button class="g-recaptcha" data-callback="biscolabLaravelReCaptcha" data-sitekey="api_site_key">Custom Text</button>',
			$html_button
		);
	}

	/**
	 * @test
	 */
	public function testReCaptchaV2HtmlFormSnippet()
	{

		$recaptcha = $this->recaptcha_v2;
		$html_snippet = $recaptcha->htmlFormSnippet();
		$this->assertEquals('<div class="g-recaptcha" data-sitekey="api_site_key" data-size="normal" data-theme="light" id="recaptcha-element"></div>', $html_snippet);
	}

	/**
	 * @test
	 * @expectedException     \Error
	 */
	public function testReCaptchaInvisibleHtmlFormSnippetShouldThrowError()
	{
		$this->expectException('\Error');
		$this->recaptcha_invisible->htmlFormSnippet();
	}

	/**
	 * @test
	 */
	public function testSkipByIpAndReturnArrayReturnsDefaultArray()
	{

		$mock = $this->getMockBuilder(ReCaptchaBuilder::class)
			->setConstructorArgs([
				"api_site_key",
				"api_secret_key"
			])
			->setMethods([
				'returnArray'
			])
			->getMock();

		$mock->method('returnArray')
			->willReturn(true);

		$this->setSkipByIp($this->recaptcha_v3, true);

		$validate = $this->recaptcha_v3->validate("");

		$this->assertEquals([
			"skip_by_ip" => true,
			"score"      => 0.9,
			"success"    => true
		], $validate);
	}

	/**
	 * @test
	 */
	public function testSkipByIpReturnsValidResponse()
	{

		$this->setSkipByIp($this->recaptcha_invisible, true);
		$validate = $this->recaptcha_invisible->validate("");

		$this->assertTrue($validate);
	}

	/**
	 * @test
	 */
	public function testDefaultCurlTimeout()
	{

		$this->assertEquals($this->recaptcha_invisible->getCurlTimeout(), ReCaptchaBuilder::DEFAULT_CURL_TIMEOUT);
		$this->assertEquals($this->recaptcha_v2->getCurlTimeout(), ReCaptchaBuilder::DEFAULT_CURL_TIMEOUT);
		$this->assertEquals($this->recaptcha_v3->getCurlTimeout(), ReCaptchaBuilder::DEFAULT_CURL_TIMEOUT);
	}

	/**
	 * @test
	 * @expectedException     \Error
	 */
	public function testReCaptchaV2htmlFormButtonShouldThrowError()
	{
		$this->expectException('\Error');
		$this->recaptcha_v2->htmlFormButton();
	}

	/**
	 * @test
	 */
	public function testRecaptchaFieldNameHelperReturnsReCaptchaBuilderDefaultFieldName()
	{
		$this->assertEquals(ReCaptchaBuilder::DEFAULT_RECAPTCHA_FIELD_NAME, recaptchaFieldName());
	}

	/**
	 * @test
	 */
	public function testRecaptchaRuleNameHelperReturnsReCaptchaBuilderDefaultRuleName()
	{
		$this->assertEquals(ReCaptchaBuilder::DEFAULT_RECAPTCHA_RULE_NAME, recaptchaRuleName());
	}

	/**
	 * @test
	 */
	public function testDefaultRecaptchaApiDomainIsGoogleDotCom()
	{
		$this->assertEquals("www.google.com", $this->recaptcha_v2->getApiDomain());
		$this->assertEquals("www.google.com", $this->recaptcha_invisible->getApiDomain());
		$this->assertEquals("www.google.com", $this->recaptcha_v3->getApiDomain());
	}

	protected function setSkipByIp(ReCaptchaBuilder $builder, bool $value)
	{

		$reflection = new \ReflectionClass($builder);
		$reflection_property = $reflection->getProperty('skip_by_ip');
		$reflection_property->setAccessible(true);
		$reflection_property->setValue($builder, $value);
	}

	/**
	 * @inheritdoc
	 */
	protected function setUp(): void
	{

		parent::setUp(); // TODO: Change the autogenerated stub

		$this->recaptcha_invisible = new ReCaptchaBuilderInvisible('api_site_key', 'api_secret_key');
		$this->recaptcha_v2 = new ReCaptchaBuilderV2('api_site_key', 'api_secret_key');
		$this->recaptcha_v3 = new ReCaptchaBuilderV3('api_site_key', 'api_secret_key');
	}
}


================================================
FILE: tests/ReCaptchaV3Test.php
================================================
<?php

/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - ReCaptchaV3Test.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 22/1/2019
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\Controllers\ReCaptchaController;
use Biscolab\ReCaptcha\Facades\ReCaptcha;
use Biscolab\ReCaptcha\ReCaptchaBuilderV3;
use Illuminate\Support\Facades\App;

/**
 * Class ReCaptchaV3Test
 * @package Biscolab\ReCaptcha\Tests
 */
class ReCaptchaV3Test extends TestCase
{

    protected $recaptcha_v3 = null;

    /**
     * @test
     */
    public function testGetApiVersion()
    {

        $this->assertEquals($this->recaptcha_v3->getVersion(), 'v3');
    }

    /**
     * @test
     */
    public function testAction()
    {

        $r = $this->recaptcha_v3->htmlScriptTagJsApi(['action' => 'someAction']);
        $this->assertMatchesRegularExpression('/someAction/', $r);
    }

    /**
     * @test
     */
    public function testFetchCallbackFunction()
    {

        $r = $this->recaptcha_v3->htmlScriptTagJsApi(['callback_then' => 'functionCallbackThen']);
        $this->assertMatchesRegularExpression('/functionCallbackThen\(response\)/', $r);
    }

    /**
     * @test
     */
    public function testcCatchCallbackFunction()
    {

        $r = $this->recaptcha_v3->htmlScriptTagJsApi(['callback_catch' => 'functionCallbackCatch']);
        $this->assertMatchesRegularExpression('/functionCallbackCatch\(err\)/', $r);
    }

    /**
     * @test
     */
    public function testCustomValidationFunction()
    {

        $r = $this->recaptcha_v3->htmlScriptTagJsApi(['custom_validation' => 'functionCustomValidation']);
        $this->assertMatchesRegularExpression('/functionCustomValidation\(token\)/', $r);
    }

    /**
     * @test
     */
    public function testCustomValidationUrl()
    {

        $r = $this->recaptcha_v3->htmlScriptTagJsApi();
        $this->assertMatchesRegularExpression('/http:\/\/localhost\/my-custom-url\?my-custom-token-name/', $r);
    }

    /**
     * @test
     */
    public function testValidateController()
    {

        $controller = App::make(ReCaptchaController::class);
        $return = $controller->validateV3();

        $this->assertArrayHasKey("success", $return);
        $this->assertArrayHasKey("error-codes", $return);
    }

    /**
     * @test
     */
    public function testCurlTimeoutIsSet()
    {

        $this->assertEquals($this->recaptcha_v3->getCurlTimeout(), 3);
    }

    /**
     * @test
     */
    public function testHtmlScriptTagJsApiCalledByFacade()
    {

        ReCaptcha::shouldReceive('htmlScriptTagJsApi')
            ->once()
            ->with([]);

        htmlScriptTagJsApi([]);
    }

    /**
     * @test
     */
    public function testValidationUrlShouldBeMyCustomUrl()
    {
        $this->assertEquals($this->recaptcha_v3->getValidationUrl(), "http://localhost/my-custom-url");
    }

    /**
     * @test
     */
    public function testTokenParamNameShouldBeMyCustomTokenParamName()
    {
        $this->assertEquals($this->recaptcha_v3->getTokenParameterName(), "my-custom-token-name");
    }

    /**
     * @test
     */
    public function testValidationUrlShouldBeMyCustomValidationUrl()
    {
        $this->assertEquals($this->recaptcha_v3->getValidationUrlWithToken(), "http://localhost/my-custom-url?my-custom-token-name");
    }

    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application $app
     *
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {

        $app['config']->set('recaptcha.version', 'v3');
        $app['config']->set('recaptcha.curl_timeout', 3);

        $app['config']->set('recaptcha.default_validation_route', "my-custom-url");
        $app['config']->set('recaptcha.default_token_parameter_name', "my-custom-token-name");
    }

    /**
     * Setup the test environment.
     */
    protected function setUp(): void
    {

        parent::setUp(); // TODO: Change the autogenerated stub
        $this->recaptcha_v3 = new ReCaptchaBuilderV3('api_site_key', 'api_secret_key');
    }
}


================================================
FILE: tests/TestCase.php
================================================
<?php
/**
 * Copyright (c) 2017 - present
 * LaravelGoogleRecaptcha - TestCase.php
 * author: Roberto Belotti - roby.belotti@gmail.com
 * web : robertobelotti.com, github.com/biscolab
 * Initial version created on: 12/9/2018
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
 */

namespace Biscolab\ReCaptcha\Tests;

use Biscolab\ReCaptcha\Facades\ReCaptcha;
use Biscolab\ReCaptcha\ReCaptchaServiceProvider;
use Orchestra\Testbench\TestCase as OrchestraTestCase;

/**
 * Class TestCase
 * @package Biscolab\ReCaptcha\Tests
 */
class TestCase extends OrchestraTestCase
{

	/**
	 * Load package alias
	 *
	 * @param  \Illuminate\Foundation\Application $app
	 *
	 * @return array
	 */
	protected function getPackageAliases($app)
	{

		return [
			'ReCaptcha' => ReCaptcha::class,
		];
	}

	/**
	 * Load package service provider
	 *
	 * @param \Illuminate\Foundation\Application $app
	 *
	 * @return array
	 */
	protected function getPackageProviders($app)
	{

		return [ReCaptchaServiceProvider::class];
	}
}
Download .txt
gitextract_5ohrg6fm/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── pull_request_template.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── config/
│   └── recaptcha.php
├── phpunit.xml.dist
├── src/
│   ├── Controllers/
│   │   └── ReCaptchaController.php
│   ├── Exceptions/
│   │   └── InvalidConfigurationException.php
│   ├── Facades/
│   │   └── ReCaptcha.php
│   ├── ReCaptchaBuilder.php
│   ├── ReCaptchaBuilderInvisible.php
│   ├── ReCaptchaBuilderV2.php
│   ├── ReCaptchaBuilderV3.php
│   ├── ReCaptchaServiceProvider.php
│   └── helpers.php
└── tests/
    ├── ReCaptchaConfigurationTest.php
    ├── ReCaptchaCustomApiDomainTest.php
    ├── ReCaptchaHelpersInvisibleTest.php
    ├── ReCaptchaHelpersV2ExplicitTest.php
    ├── ReCaptchaHelpersV2Test.php
    ├── ReCaptchaInvalidConfigurationTest.php
    ├── ReCaptchaLangTest.php
    ├── ReCaptchaTest.php
    ├── ReCaptchaV3Test.php
    └── TestCase.php
Download .txt
SYMBOL INDEX (134 symbols across 19 files)

FILE: src/Controllers/ReCaptchaController.php
  class ReCaptchaController (line 19) | class ReCaptchaController extends Controller
    method validateV3 (line 25) | public function validateV3(): array

FILE: src/Exceptions/InvalidConfigurationException.php
  class InvalidConfigurationException (line 13) | class InvalidConfigurationException extends \Exception

FILE: src/Facades/ReCaptcha.php
  class ReCaptcha (line 24) | class ReCaptcha extends Facade
    method getFacadeAccessor (line 32) | protected static function getFacadeAccessor()

FILE: src/ReCaptchaBuilder.php
  class ReCaptchaBuilder (line 21) | class ReCaptchaBuilder
    method __construct (line 106) | public function __construct(
    method setApiSiteKey (line 125) | public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder
    method setApiSecretKey (line 138) | public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder
    method getCurlTimeout (line 149) | public function getCurlTimeout(): int
    method setVersion (line 160) | public function setVersion(string $version): ReCaptchaBuilder
    method getVersion (line 171) | public function getVersion(): string
    method setSkipByIp (line 182) | public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder
    method setApiDomain (line 195) | public function setApiDomain(?string $api_domain = null): ReCaptchaBui...
    method getApiDomain (line 206) | public function getApiDomain(): string
    method setApiUrls (line 215) | public function setApiUrls(): ReCaptchaBuilder
    method getIpWhitelist (line 227) | public function getIpWhitelist()
    method skipByIp (line 249) | public function skipByIp(): bool
    method htmlScriptTagJsApi (line 263) | public function htmlScriptTagJsApi(?array $configuration = []): string
    method validate (line 303) | public function validate($response)
    method getApiSiteKey (line 363) | public function getApiSiteKey(): string
    method getApiSecretKey (line 372) | public function getApiSecretKey(): string
    method returnArray (line 381) | protected function returnArray(): bool
    method getOnLoadCallback (line 390) | public function getOnLoadCallback(): string

FILE: src/ReCaptchaBuilderInvisible.php
  class ReCaptchaBuilderInvisible (line 20) | class ReCaptchaBuilderInvisible extends ReCaptchaBuilder
    method __construct (line 34) | public function __construct(string $api_site_key, string $api_secret_key)
    method htmlFormButton (line 49) | public function htmlFormButton($button_label = 'Submit', ?array $prope...
    method htmlScriptTagJsApi (line 92) | public function htmlScriptTagJsApi(?array $configuration = []): string
    method getFormId (line 116) | public function getFormId(): string

FILE: src/ReCaptchaBuilderV2.php
  class ReCaptchaBuilderV2 (line 21) | class ReCaptchaBuilderV2 extends ReCaptchaBuilder
    method __construct (line 39) | public function __construct(string $api_site_key, string $api_secret_key)
    method htmlFormSnippet (line 52) | public function htmlFormSnippet(?array $attributes = []): string
    method getTagAttributes (line 73) | public function getTagAttributes(): array
    method getOnLoadCallback (line 100) | public function getOnLoadCallback(): string
    method cleanAttributes (line 114) | public static function cleanAttributes(?array $attributes = []): array

FILE: src/ReCaptchaBuilderV3.php
  class ReCaptchaBuilderV3 (line 20) | class ReCaptchaBuilderV3 extends ReCaptchaBuilder
    method __construct (line 29) | public function __construct(string $api_site_key, string $api_secret_key)
    method getTokenParameterName (line 35) | public function getTokenParameterName(): string
    method getValidationUrl (line 43) | public function getValidationUrl(): string
    method getValidationUrlWithToken (line 51) | public function getValidationUrlWithToken(): string
    method htmlScriptTagJsApi (line 70) | public function htmlScriptTagJsApi(?array $configuration = []): string

FILE: src/ReCaptchaServiceProvider.php
  class ReCaptchaServiceProvider (line 22) | class ReCaptchaServiceProvider extends ServiceProvider
    method boot (line 35) | public function boot()
    method addValidationRule (line 48) | public function addValidationRule()
    method register (line 66) | public function register()
    method provides (line 82) | public function provides(): array
    method registerRoutes (line 93) | protected function registerRoutes(): ReCaptchaServiceProvider
    method registerReCaptchaBuilder (line 109) | protected function registerReCaptchaBuilder()

FILE: src/helpers.php
  function recaptcha (line 18) | function recaptcha(): \Biscolab\ReCaptcha\ReCaptchaBuilder
  function htmlScriptTagJsApi (line 39) | function htmlScriptTagJsApi(?array $config = []): string
  function htmlFormButton (line 63) | function htmlFormButton(?string $button_label = 'Submit', ?array $proper...
  function htmlFormSnippet (line 83) | function htmlFormSnippet(?array $attributes = []): string
  function getFormId (line 100) | function getFormId(): string
  function recaptchaRuleName (line 116) | function recaptchaRuleName(): string
  function recaptchaFieldName (line 132) | function recaptchaFieldName(): string

FILE: tests/ReCaptchaConfigurationTest.php
  class ReCaptchaConfigurationTest (line 20) | class ReCaptchaConfigurationTest extends TestCase
    method testGetApiSiteKey (line 31) | public function testGetApiSiteKey() {
    method testGetApiSecretKey (line 38) | public function testGetApiSecretKey() {
    method testSkipIpWhiteListIsArray (line 45) | public function testSkipIpWhiteListIsArray()
    method testCurlTimeoutIsSet (line 59) | public function testCurlTimeoutIsSet()
    method getEnvironmentSetUp (line 72) | protected function getEnvironmentSetUp($app)
    method setUp (line 84) | protected function setUp(): void

FILE: tests/ReCaptchaCustomApiDomainTest.php
  class ReCaptchaCustomApiDomainTest (line 18) | class ReCaptchaCustomApiDomainTest extends TestCase
    method testRecaptchaApiDomainChangesByConfig (line 39) | public function testRecaptchaApiDomainChangesByConfig()
    method testRecaptchaApiDomainChangesByConfigInHtmlScriptTagJsApi (line 50) | public function testRecaptchaApiDomainChangesByConfigInHtmlScriptTagJs...
    method getEnvironmentSetUp (line 64) | protected function getEnvironmentSetUp($app)
    method setUp (line 73) | protected function setUp(): void

FILE: tests/ReCaptchaHelpersInvisibleTest.php
  class ReCaptchaHelpersInvisibleTest (line 16) | class ReCaptchaHelpersInvisibleTest extends TestCase
    method testHtmlScriptTagJsApiCalledByFacade (line 22) | public function testHtmlScriptTagJsApiCalledByFacade()
    method testHtmlFormButtonCalledByFacade (line 35) | public function testHtmlFormButtonCalledByFacade()
    method testGetFormIdCalledByFacade (line 48) | public function testGetFormIdCalledByFacade()
    method testHtmlFormButtonConfiguration (line 57) | public function testHtmlFormButtonConfiguration()
    method testHtmlFormSnippetCalledByFacade (line 68) | public function testHtmlFormSnippetCalledByFacade()
    method testGetFormIdReturnDefaultFormIdValue (line 78) | public function testGetFormIdReturnDefaultFormIdValue()
    method getEnvironmentSetUp (line 90) | protected function getEnvironmentSetUp($app)

FILE: tests/ReCaptchaHelpersV2ExplicitTest.php
  class ReCaptchaHelpersV2ExplicitTest (line 16) | class ReCaptchaHelpersV2ExplicitTest extends TestCase
    method testGetOnLoadCallbackFunction (line 22) | public function testGetOnLoadCallbackFunction()
    method testHtmlScriptTagJsApiHasJavascriptRenderFunction (line 38) | public function testHtmlScriptTagJsApiHasJavascriptRenderFunction()
    method testTagAttributes (line 52) | public function testTagAttributes()
    method testExpectReCaptchaInstanceOfReCaptchaBuilderV2 (line 79) | public function testExpectReCaptchaInstanceOfReCaptchaBuilderV2()
    method testHtmlFormSnippet (line 88) | public function testHtmlFormSnippet()
    method getEnvironmentSetUp (line 106) | protected function getEnvironmentSetUp($app)

FILE: tests/ReCaptchaHelpersV2Test.php
  class ReCaptchaHelpersV2Test (line 17) | class ReCaptchaHelpersV2Test extends TestCase
    method testHtmlScriptTagJsApiCalledByFacade (line 23) | public function testHtmlScriptTagJsApiCalledByFacade()
    method testHtmlFormSnippetCalledByFacade (line 36) | public function testHtmlFormSnippetCalledByFacade()
    method testTagAttributes (line 48) | public function testTagAttributes()
    method testExpectReCaptchaInstanceOfReCaptchaBuilderV2 (line 75) | public function testExpectReCaptchaInstanceOfReCaptchaBuilderV2()
    method testExpectExceptionWhenGetFormIdFunctionIsCalled (line 84) | public function testExpectExceptionWhenGetFormIdFunctionIsCalled()
    method testHtmlFormSnippet (line 93) | public function testHtmlFormSnippet()
    method testHtmlFormSnippetOverridesDefaultAttributes (line 106) | public function testHtmlFormSnippetOverridesDefaultAttributes()
    method testCleanAttributesReturnsOnlyAllowedAttributes (line 127) | public function testCleanAttributesReturnsOnlyAllowedAttributes()
    method testHtmlFormSnippetKeepsDefaultConfigValuesUnlessOtherwiseStated (line 150) | public function testHtmlFormSnippetKeepsDefaultConfigValuesUnlessOther...
    method getEnvironmentSetUp (line 170) | protected function getEnvironmentSetUp($app)

FILE: tests/ReCaptchaInvalidConfigurationTest.php
  class ReCaptchaInvalidConfigurationTest (line 20) | class ReCaptchaInvalidConfigurationTest extends TestCase
    method testV2HtmlScriptTagJsApiThrowsInvalidConfigurationException (line 26) | public function testV2HtmlScriptTagJsApiThrowsInvalidConfigurationExce...
    method getEnvironmentSetUp (line 41) | protected function getEnvironmentSetUp($app)

FILE: tests/ReCaptchaLangTest.php
  class ReCaptchaLangTest (line 17) | class ReCaptchaLangTest extends TestCase
    method testHtmlScriptTagJsApiGetHtmlScriptWithHlParam (line 33) | public function testHtmlScriptTagJsApiGetHtmlScriptWithHlParam()
    method testHtmlScriptTagJsApiGetHtmlScriptOverridingHlParam (line 43) | public function testHtmlScriptTagJsApiGetHtmlScriptOverridingHlParam()
    method getEnvironmentSetUp (line 57) | protected function getEnvironmentSetUp($app)
    method setUp (line 66) | protected function setUp(): void

FILE: tests/ReCaptchaTest.php
  class ReCaptchaTest (line 24) | class ReCaptchaTest extends TestCase
    method testHtmlScriptTagJsApiGetHtmlScriptTag (line 45) | public function testHtmlScriptTagJsApiGetHtmlScriptTag()
    method testReCaptchaInvisibleHtmlFormButtonDefault (line 55) | public function testReCaptchaInvisibleHtmlFormButtonDefault()
    method testReCaptchaInvisibleHtmlFormButtonCustom (line 69) | public function testReCaptchaInvisibleHtmlFormButtonCustom()
    method testReCaptchaV2HtmlFormSnippet (line 83) | public function testReCaptchaV2HtmlFormSnippet()
    method testReCaptchaInvisibleHtmlFormSnippetShouldThrowError (line 95) | public function testReCaptchaInvisibleHtmlFormSnippetShouldThrowError()
    method testSkipByIpAndReturnArrayReturnsDefaultArray (line 104) | public function testSkipByIpAndReturnArrayReturnsDefaultArray()
    method testSkipByIpReturnsValidResponse (line 134) | public function testSkipByIpReturnsValidResponse()
    method testDefaultCurlTimeout (line 146) | public function testDefaultCurlTimeout()
    method testReCaptchaV2htmlFormButtonShouldThrowError (line 158) | public function testReCaptchaV2htmlFormButtonShouldThrowError()
    method testRecaptchaFieldNameHelperReturnsReCaptchaBuilderDefaultFieldName (line 167) | public function testRecaptchaFieldNameHelperReturnsReCaptchaBuilderDef...
    method testRecaptchaRuleNameHelperReturnsReCaptchaBuilderDefaultRuleName (line 175) | public function testRecaptchaRuleNameHelperReturnsReCaptchaBuilderDefa...
    method testDefaultRecaptchaApiDomainIsGoogleDotCom (line 183) | public function testDefaultRecaptchaApiDomainIsGoogleDotCom()
    method setSkipByIp (line 190) | protected function setSkipByIp(ReCaptchaBuilder $builder, bool $value)
    method setUp (line 202) | protected function setUp(): void

FILE: tests/ReCaptchaV3Test.php
  class ReCaptchaV3Test (line 23) | class ReCaptchaV3Test extends TestCase
    method testGetApiVersion (line 31) | public function testGetApiVersion()
    method testAction (line 40) | public function testAction()
    method testFetchCallbackFunction (line 50) | public function testFetchCallbackFunction()
    method testcCatchCallbackFunction (line 60) | public function testcCatchCallbackFunction()
    method testCustomValidationFunction (line 70) | public function testCustomValidationFunction()
    method testCustomValidationUrl (line 80) | public function testCustomValidationUrl()
    method testValidateController (line 90) | public function testValidateController()
    method testCurlTimeoutIsSet (line 103) | public function testCurlTimeoutIsSet()
    method testHtmlScriptTagJsApiCalledByFacade (line 112) | public function testHtmlScriptTagJsApiCalledByFacade()
    method testValidationUrlShouldBeMyCustomUrl (line 125) | public function testValidationUrlShouldBeMyCustomUrl()
    method testTokenParamNameShouldBeMyCustomTokenParamName (line 133) | public function testTokenParamNameShouldBeMyCustomTokenParamName()
    method testValidationUrlShouldBeMyCustomValidationUrl (line 141) | public function testValidationUrlShouldBeMyCustomValidationUrl()
    method getEnvironmentSetUp (line 153) | protected function getEnvironmentSetUp($app)
    method setUp (line 166) | protected function setUp(): void

FILE: tests/TestCase.php
  class TestCase (line 21) | class TestCase extends OrchestraTestCase
    method getPackageAliases (line 31) | protected function getPackageAliases($app)
    method getPackageProviders (line 46) | protected function getPackageProviders($app)
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (110K chars).
[
  {
    "path": ".editorconfig",
    "chars": 145,
    "preview": "[*]\ncharset=utf-8\nend_of_line=lf\ninsert_final_newline=false\nindent_style=space\nindent_size=4\n\n[{*.yml,*.yaml}]\nindent_st"
  },
  {
    "path": ".gitattributes",
    "chars": 332,
    "preview": "/tests export-ignore\n/.github export-ignore\n/.gitattributes export-ignore\n/.gitignore export-ignore\n/.scrutinizer.yml ex"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 759,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/pull_request_template.md",
    "chars": 921,
    "preview": "Bug Fixes **ONLY**. NO NEW FEATURE ACCEPTED!\n\n<!--- Provide a general summary of your changes in the Title above -->\n\n##"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1181,
    "preview": "name: CI\n\non:\n  - push\n  - pull_request\n\njobs:\n  run:\n    runs-on: ${{ matrix.os }}\n\n    strategy:\n      matrix:\n       "
  },
  {
    "path": ".gitignore",
    "chars": 302,
    "preview": "vendor/\n/.idea\n\n# Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer\n.rocketeer/\n"
  },
  {
    "path": ".scrutinizer.yml",
    "chars": 283,
    "preview": "build:\n  nodes:\n    coverage:\n      tests:\n        override:\n          - command: vendor/bin/phpunit --coverage-clover=c"
  },
  {
    "path": ".travis.yml",
    "chars": 956,
    "preview": "language: php\n\nphp:\n  - 7.3\n  - 7.4\n  - 8.0\n# PHP 8.1 isn't currently support by Travis CI\n#  - 8.1\n\nenv:\n  - ILLUMINATE"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3219,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1341,
    "preview": "# Contributing\n\nContributions are **welcome** and will be fully **credited**.\n\nWe accept contributions via Pull Requests"
  },
  {
    "path": "LICENSE",
    "chars": 1082,
    "preview": "MIT License\n\nCopyright (c) 2017 - present Roberto Belotti\n\nPermission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "README.md",
    "chars": 26580,
    "preview": "**Laravel ReCAPTCHA** is a very simply-to-use Laravel 5 package to embed Google reCAPTCHA in your application.\n\n[![Build"
  },
  {
    "path": "composer.json",
    "chars": 1589,
    "preview": "{\n    \"name\": \"biscolab/laravel-recaptcha\",\n    \"description\": \"Simple and painless Google reCAPTCHA package for Laravel"
  },
  {
    "path": "config/recaptcha.php",
    "chars": 5049,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - recaptcha.php\n * author: Roberto Belotti - roby.b"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 816,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" backupGlobals=\"fal"
  },
  {
    "path": "src/Controllers/ReCaptchaController.php",
    "chars": 735,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaController.php\n * author: Roberto Belotti"
  },
  {
    "path": "src/Exceptions/InvalidConfigurationException.php",
    "chars": 433,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - InvalidConfigurationException.php\n * author: Rober"
  },
  {
    "path": "src/Facades/ReCaptcha.php",
    "chars": 920,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptcha.php\n * author: Roberto Belotti - roby.be"
  },
  {
    "path": "src/ReCaptchaBuilder.php",
    "chars": 8843,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaBuilder.php\n * author: Roberto Belotti -"
  },
  {
    "path": "src/ReCaptchaBuilderInvisible.php",
    "chars": 3365,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaBuilderInvisible.php\n * author: Roberto B"
  },
  {
    "path": "src/ReCaptchaBuilderV2.php",
    "chars": 3714,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaBuilderV2.php\n * author: Roberto Belotti"
  },
  {
    "path": "src/ReCaptchaBuilderV3.php",
    "chars": 3526,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaBuilderV3.php\n * author: Roberto Belotti"
  },
  {
    "path": "src/ReCaptchaServiceProvider.php",
    "chars": 3135,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaServiceProvider.php\n * author: Roberto B"
  },
  {
    "path": "src/helpers.php",
    "chars": 3121,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - helpers.php\n * author: Roberto Belotti - roby.bel"
  },
  {
    "path": "tests/ReCaptchaConfigurationTest.php",
    "chars": 1976,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaConfigurationTest.php\n * author: Roberto "
  },
  {
    "path": "tests/ReCaptchaCustomApiDomainTest.php",
    "chars": 2486,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaCustomApiDomainTest.php\n * author: Rober"
  },
  {
    "path": "tests/ReCaptchaHelpersInvisibleTest.php",
    "chars": 2490,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaHelpersInvisibleTest.phpp\n * author: Rob"
  },
  {
    "path": "tests/ReCaptchaHelpersV2ExplicitTest.php",
    "chars": 4366,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaHelpersV2ExplicitTest.php\n * author: Rob"
  },
  {
    "path": "tests/ReCaptchaHelpersV2Test.php",
    "chars": 6190,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaHelpersV2Test.php\n * author: Roberto Bel"
  },
  {
    "path": "tests/ReCaptchaInvalidConfigurationTest.php",
    "chars": 1371,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaInvalidConfigurationTest.php\n * author: R"
  },
  {
    "path": "tests/ReCaptchaLangTest.php",
    "chars": 1749,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaLangTest.php\n * author: Roberto Belotti -"
  },
  {
    "path": "tests/ReCaptchaTest.php",
    "chars": 5176,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaTest.php\n * author: Roberto Belotti - ro"
  },
  {
    "path": "tests/ReCaptchaV3Test.php",
    "chars": 4278,
    "preview": "<?php\n\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - ReCaptchaV3Test.php\n * author: Roberto Belotti - "
  },
  {
    "path": "tests/TestCase.php",
    "chars": 1038,
    "preview": "<?php\n/**\n * Copyright (c) 2017 - present\n * LaravelGoogleRecaptcha - TestCase.php\n * author: Roberto Belotti - roby.bel"
  }
]

About this extraction

This page contains the full source code of the biscolab/laravel-recaptcha GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (101.0 KB), approximately 25.7k tokens, and a symbol index with 134 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!