Update 3: A generalization for any number of lists and any column as the key:
ClearAll[combineBy];
combineBy[lists : __List, col_Integer] /; (col <= Min[Length /@ # & /@ {lists}]) :=
With[{intNodes = Alternatives @@ Intersection @@ (#[[col]] & /@ # & /@ {lists}),
joined = GatherBy[Join[lists], #[[col]] &],
othercols = DeleteCases[Range[Min[Length /@ # & /@ {lists}]], col]},
{#[[1, col]], Join @@ #[[All, othercols]]} & /@
Pick[joined, ! FreeQ[#[[1, col]], intNodes] & /@ joined]]
OP's example:
list1 = {{1, 1}, {2, 4}, {3, 9}, {4, 16}};
list2 = {{2, 6}, {3, 9}, {4, 12}, {5, 15}, {5, 7}};
combineBy[list1, list2, 1]
(* {{2, {4, 6}}, {3, {9, 9}}, {4, {16, 12}}} *)
combineBy[list1, list2, 2]
(* {{9, {3, 3}}} *)
More examples:
list3 = Table[RandomSample[Range[7], 3], {3}];
list4 = Table[RandomSample[Range[7], 3], {4}];
list5 = Table[RandomSample[Range[7], 3], {6}];
Prepend[Prepend[{SpanFromAbove, SpanFromAbove, #,
Column[combineBy[list4, list5, #]]} & /@ {2, 3},
{Column@list4, Column@list5, 1, Column[combineBy[list4, list5, 1]]}],
{"list4", "list5", "key column", "result"}] //
Grid[#, Alignment -> {Center, Center}, Dividers -> All] &

Prepend[Prepend[{SpanFromAbove, SpanFromAbove, #,
Column[combineBy[list3, list4, #]]} & /@ {2, 3},
{Column@list3, Column@list4, 1, Column[combineBy[list3, list4, 1]]}],
{"list3", "list4", "key column", "result"}] //
Grid[#, Alignment -> {Center, Center}, Dividers -> All] &

Prepend[Prepend[{SpanFromAbove, SpanFromAbove, #,
Column[combineBy[list3, list5, #]]} & /@ {2, 3},
{Column@list3, Column@list5, 1, Column[combineBy[list3, list5, 1]]}],
{"list3", "list5", "key column", "result"}] //
Grid[#, Alignment -> {Center, Center}, Dividers -> All] &

Prepend[Prepend[{SpanFromAbove, SpanFromAbove, SpanFromAbove, #,
Column[combineBy[list3, list4, list5, #]]} & /@ {2, 3},
{Column@list3, Column@list4, Column@list5, 1,
Column[combineBy[list3, list4, list5, 1]]}],
{"list3", "list4", "list5", "key column", "result"}] //
Grid[#, Alignment -> {Center, Center}, Dividers -> All] &

ClearAll[combine];
combine[list1_List, list2_List] :=
With[{intNodes = Intersection[First /@ list1, First /@ list2],
joined = GatherBy[Join[list1, list2], First]},
{First[First@#], Last[#]} & /@ (Transpose /@
Select[joined, MemberQ[intNodes, #[[1, 1]]] &])]
combine[list1, list2]
(* {{2, {4, 6}}, {3, {9, 9}}, {4, {16, 12}}} *)
combine[list1, {{2, 6}, {3, 9}, {4, 12}, {5, 15}, {5, 7}}]
(* {{2, {4, 6}}, {3, {9, 9}}, {4, {16, 12}}} *)
(Updated with correction thanks to @Mr.W's comment: the second argument of Select is changed from Length[#]>2& in the original post to the correct version that accounts for the intersection of the first columns of the two lists.)
Update 2: Using Pick instead of Select:
ClearAll[combine2];
combine2[list1_List, list2_List] :=
With[{intNodes = Intersection[First /@ list1, First /@ list2],
joined = GatherBy[Join[list1, list2], First]},
{#[[1, 1]], #[[-1]]} & /@
(Transpose /@ Pick[joined, MemberQ[intNodes, #[[1, 1]]] & /@ joined])]